├── .editorconfig
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── .tool-versions
├── CHANGELOG.md
├── CONTRIBUTE.md
├── DEVELOPMENT.md
├── LICENCE
├── README.md
├── deno.json
├── deno.lock
├── glacier-logo.png
├── gleam.toml
├── import_map.json
├── manifest.toml
├── src
├── glacier.gleam
├── glacier
│ └── should.gleam
├── glacier_demo
│ ├── glacier_demo_module_a.gleam
│ ├── glacier_demo_module_aliasing_c.gleam
│ ├── glacier_demo_module_b.gleam
│ ├── glacier_demo_module_c.gleam
│ ├── glacier_demo_module_d.gleam
│ ├── glacier_demo_module_e.gleam
│ ├── glacier_demo_module_unqualifying.gleam
│ ├── glacier_demo_references_a.gleam
│ ├── hello.gleam
│ └── other.txt
├── glacier_ffi.erl
└── glacier_ffi.mjs
└── test
├── glacier_demo
├── glacier_demo_module_a_test.gleam
├── glacier_demo_module_b_test.gleam
├── glacier_demo_module_c_test.gleam
└── glacier_demo_module_gleeunit_should.gleam
└── glacier_test.gleam
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | # top-most EditorConfig file
6 | root = true
7 |
8 | # Matches multiple files with brace expansion notation
9 | # Set default charset
10 | [*]
11 | end_of_line = lf
12 | charset = utf-8
13 | trim_trailing_whitespace = true
14 | insert_final_newline = true
15 | indent_style = space
16 | indent_size = 2
17 |
18 | [*.{rs, gleam, erl, hrl, ex, mjs, js, ts, sh}]
19 | max_line_length = 80
20 |
21 | [*.{rs, .erl, .hrl}]
22 | indent_size = 4
23 |
24 | [Makefile]
25 | indent_style = tab
26 | indent_size = 4
27 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - main
8 | pull_request:
9 |
10 | jobs:
11 | test:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3.2.0
15 | - uses: erlef/setup-beam@v1.15.0
16 | with:
17 | otp-version: "27.3.3"
18 | gleam-version: "1.9.1"
19 | rebar3-version: "3"
20 | - run: gleam format --check src test
21 | - run: gleam deps download
22 | - run: gleam test
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 | *.ez
3 | build
4 | erl_crash.dump
5 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | deno 1.42.2
2 | erlang 27.3.3
3 | gleam 1.9.1
4 | nodejs 18.20.2
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.3.2
4 |
5 | - `get_cwd_as_binary` bugfix.
6 |
7 | ## 1.3.1
8 |
9 | - updated gleam community colour to allow more recent versions of other
10 | dependencies such as gleam_json.
11 |
12 | ## 1.3.0
13 |
14 | - updated towards glacier_gleeunit 1.3.0 which uses gleeunit 1.3.0 as a basis.
15 |
16 | ## 1.1.0
17 |
18 | - Updated upstream gleeunit dependency to 1.2.0 (glacier_gleeunit 1.2.1001).
19 | - Update simplifile dep to >- 2.0.0.
20 |
21 | ## 1.0.0
22 |
23 | - Updates glacier_gleeunit dependency to up to date version.
24 |
25 | ## 0.10.0
26 |
27 | - Updates Glacier for Gleam 1.0.
28 |
29 | ## 0.9.0
30 |
31 | - Syntax updates.
32 |
33 | ## 0.8.1
34 |
35 | - Improve readme
36 | - Hide internal modules from docs
37 | - Improve docs
38 |
39 | ## 0.8.0
40 |
41 | - Upgraded to be in line with gleam 0.27.0
42 |
43 | ## 0.7.0 - skipped
44 |
45 | ## 0.6.2
46 |
47 | - Add docs on how to run glacier with deno
48 |
49 | ## 0.6.1
50 |
51 | - Attempt to fix deno testing.
52 |
53 | ## 0.6.0
54 |
55 | - With Gleam 0.26 supporting *Deno*, so does Glacier
56 |
57 | ## 0.5.0
58 |
59 | - Added fixes and work arounds so that glacier runs with Gleam 0.26.0
60 | - Glacier might run on Deno might (or might not) run once Gleam 0.26.1 is out
61 |
62 | ## 0.4.1
63 |
64 | - Fix readme.
65 |
66 | ## 0.4.0
67 |
68 | - Remove internal verbatim copy to `gleeunit` and depend on a fork:
69 | [`glacier_gleeunit`](https://hex.pm/glacier_gleeunit).
70 |
71 | ## 0.3.1
72 |
73 | - Fix Changelog.
74 |
75 | ## 0.3.0
76 |
77 | - Fix detection of unqualified imports.
78 | - Fix detection of aliased imports.
79 | - Fix detection of renames.
80 | - Files with white spaces are now ignored.
81 | - Improved readme.
82 | - Force rebased on `lpil/gleeunit` to keep all contributor history alive.
83 | - This was required because initially `Glacier` was supposed to depend on
84 | a patched `Gleeunit`, but the idea of patching `Gleeunit` a bit was
85 | rejected by the `Gleeunit` team. While this may not be strictly necessary
86 | this honors previous contributions transparently.
87 | - This was a quick and dirty process where I saved a lot of time by _not_
88 | fixing any merges and instead just adding them without touching them
89 | including the diff noise. As a consequence single commits if checked out
90 | might not run any more. After the dirty rebase I've commited the
91 | source code state equvialent to `0.2.7` to restore functionality.
92 | - Proper BE noun instead of an AE one for the `LICENCE` file and naming.
93 |
94 | ## 0.2.7
95 |
96 | - Fix `MaxListenersExceededWarning: Possible EventEmitter memory leak detected` (#4).
97 | - Cleanup `README.md`.
98 |
99 | ## 0.2.6
100 |
101 | - Instruct to use `gleam add glacier --dev` to add as a dev dependency, only.
102 |
103 | ## 0.2.5
104 |
105 | - Readme: Explain that `gleeunit` is optional if `glacier` is installed if `gleeunit/should` is replaced by `glacier/should`.
106 |
107 | ## 0.2.4
108 |
109 | - More compatibility changes to run `glacier` alongside `gleeunit`:
110 | Added `glacier/should` which is a renamed `gleeunit/should`.
111 |
112 | ## 0.2.3
113 |
114 | - Remove code that did nothing.
115 |
116 | ## 0.2.2
117 |
118 | - Fixed `README.md`.
119 |
120 | ## 0.2.1
121 |
122 | - Fixed `README.md`.
123 |
124 | ## 0.2.0
125 |
126 | - Added `CHANGELOG.md`.
127 | - Renamed internal gleeunit fork to gleeunit2 to avoid collisions.
128 | - JavaScript: File renames are picked up by the watcher.
129 | - Replaced `glacier.run()` with `glacier.main()` as latter did not make sense before.
130 | - Fixed `README.md`.
131 | - Fixed the logo to work across hexpm and github and shields badges.
132 |
133 | ## 0.1.0
134 |
135 | - _Ex post_ rebased on `lpil/gleeunit`
136 |
137 | ### Gleeunit v0.8
138 |
139 | - `should.be_ok` and `should.be_error` now unwrap the result argument
140 |
141 | ### Gleeunit v0.7.2 - 2022-11-19
142 |
143 | - Update for Gleam v0.25.0.
144 |
145 | ### Gleeunit v0.7.1 - 2022-11-11
146 |
147 | - Fixed a bug where project names containing numbers would not run correctly on
148 | JavaScript.
149 |
150 | ### Gleeunit v0.7.0 - 2022-09-24
151 |
152 | - Line numbers are printed on JS for assertions.
153 |
154 | ### Gleeunit v0.6.2 - 2022-07-15
155 |
156 | - Fixed a bug where assertions in JavaScript tests could fail to report an
157 | error due to them being async.
158 |
159 | ### Gleeunit v0.6.1 - 2022-01-26
160 |
161 | - Fixed a bug where failed tests on the JavaScript target would crash the `main`
162 | function.
163 | - Fixed a bug where tests on the JavaScript target could succeed regardless of
164 | return value.
165 |
166 | ### Gleeunit v0.6.0 - 2022-01-09
167 |
168 | - Added support for OTP versions below 23.
169 | - Added support for running tests on the JavaScript target.
170 |
171 | ### Gleeunit v0.5.0 - 2021-12-05
172 |
173 | - Updated for Gleam v0.18.0.
174 | - Added `gleeunit/should` module containing assertions.
175 |
176 | ### Gleeunit v0.4.0 - 2021-11-01
177 |
178 | - Slightly improved failure format.
179 |
180 | ### Gleeunit v0.3.0 - 2021-10-31
181 |
182 | - Fixed Hex package which was missing some files.
183 |
184 | ### Gleeunit v0.2.0 - 2021-10-31
185 |
186 | - `gleeunit.discover_and_run_tests` removed.
187 | - `gleeunit.main` added.
188 |
189 | ### Gleeunit v0.1.0 - 2021-10-31
190 |
191 | - Initial release.
192 |
--------------------------------------------------------------------------------
/CONTRIBUTE.md:
--------------------------------------------------------------------------------
1 | # Glacier · Contribution Guide
2 |
3 | This package heavily depends on a [gleeunit](https://github.com/lpil/gleeunit) fork called [glacier_gleeunit](https://github.com/inoas/gleeunit)!
4 |
5 | ## Improving Glacier
6 |
7 | ```shell
8 | git clone https://github.com/inoas/glacier.git
9 | cd glacier
10 | ```
11 |
12 | See all [open issues](https://github.com/inoas/glacier/issues) on GitHub if you want to help out.
13 |
14 | ## How does it work?
15 |
16 | 1. `gleam test` passes through `glacier.main()` and simply executes `gleeunit.main()` as if **gleeunit** was used directly.
17 | 2. `gleam test -- test_module_a test_module_b` passes through `glacier.main()` and executes `gleeunit.test_modules(modules_list)` where `modules_list` is `["foo", "bar"]`. The given modules are checked if they exist as either `.gleam` or `.erl` test module files and then **gleeunit** runs these test modules.
18 | 3. `gleam test -- --glacier` enters `glacier.main()` and starts a file watcher: Upon changes in module files in `./test` it just passes those through as `gleam test -- changed_test_module`(so re-saving test files executes the single test), and if a module file in `./src` got changed it parses that changed module file for any imported modules and puts the module and all chained imported modules in a distinct list of modules that should be tested. Then all test module files are read and imports of those are gathered one by one and cross matched against that list. The result is a list of test modules that need to be run, which then gets done by executing a shell call similar to `gleam test -- detected_test_module_a detected_test_module_b detected_test_module_c etc`, aka jump to step `2`.
19 |
20 | ### Demo for target Erlang
21 |
22 | ```shell
23 | # Traditional test runs
24 | gleam test --target erlang
25 | gleam test --target erlang -- test/glacier_demo/glacier_demo_module_a_test.gleam
26 |
27 | # Incremental interactive test
28 | gleam test --target erlang -- --glacier
29 | # Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
30 | ```
31 |
32 | ### Demo for target JavaScript on NodeJS
33 |
34 | ```shell
35 | # Traditional test runs
36 | gleam test --target javascript --runtime node
37 | gleam test --target javascript --runtime node -- test/glacier_demo/glacier_demo_module_a_test.gleam
38 |
39 | # Incremental interactive test
40 | gleam test --target javascript --runtime node -- --glacier
41 | # Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
42 | ```
43 |
44 | Notice: You may omit `--runtime node` as it is currently set as the default runtime for target `javascript`.
45 |
46 | ### Demo for target JavaScript on Deno
47 |
48 | ```shell
49 | # Traditional test runs
50 | gleam test --target javascript --runtime deno
51 | gleam test --target javascript --runtime deno -- test/glacier_demo/glacier_demo_module_a_test.gleam
52 |
53 | # Incremental interactive test
54 | gleam test --target javascript --runtime deno -- --glacier
55 | # Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
56 | ```
57 |
58 | #### Privileges required for Deno
59 |
60 | Do not forget to edit `gleam.toml` to add deno privileges:
61 |
62 | ```toml
63 | [javascript.deno]
64 | allow_read = ["./"]
65 | allow_net = ["deno.land"]
66 | allow_run = ["gleam"]
67 | ```
68 |
69 | ### Caveats
70 |
71 | In line with gleam module name requirements:
72 |
73 | - `./src` and `./test` source files with white spaces will not pass detection.
74 | - Behavior towards `./src` and `./test` source files with non-alphanumeric characters is not tested, and generally unsupported.
75 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Glacier · Contribution Guide
2 |
3 | This package heavily depends on a [gleeunit](https://github.com/lpil/gleeunit) fork called [glacier_gleeunit](https://github.com/inoas/gleeunit)!
4 |
5 | ## Improving Glacier
6 |
7 | ```shell
8 | git clone https://github.com/inoas/glacier.git
9 | cd glacier
10 | ```
11 |
12 | See all [open issues](https://github.com/inoas/glacier/issues) on GitHub if you want to help out.
13 |
14 | ## How does it work?
15 |
16 | 1. `gleam test` passes through `glacier.main()` and simply executes `gleeunit.main()` as if **gleeunit** was used directly.
17 | 2. `gleam test -- test_module_a test_module_b` passes through `glacier.main()` and executes `gleeunit.test_modules(modules_list)` where `modules_list` is `["foo", "bar"]`. The given modules are checked if they exist as either `.gleam` or `.erl` test module files and then **gleeunit** runs these test modules.
18 | 3. `gleam test -- --glacier` enters `glacier.main()` and starts a file watcher: Upon changes in module files in `./test` it just passes those through as `gleam test -- changed_test_module`(so re-saving test files executes the single test), and if a module file in `./src` got changed it parses that changed module file for any imported modules and puts the module and all chained imported modules in a distinct list of modules that should be tested. Then all test module files are read and imports of those are gathered one by one and cross matched against that list. The result is a list of test modules that need to be run, which then gets done by executing a shell call similar to `gleam test -- detected_test_module_a detected_test_module_b detected_test_module_c etc`, aka jump to step `2`.
19 |
20 | ### Demo for target Erlang
21 |
22 | ```shell
23 | # Traditional test runs
24 | gleam test --target erlang
25 | gleam test --target erlang -- test/glacier_demo/glacier_demo_module_a_test.gleam
26 |
27 | # Incremental interactive test
28 | gleam test --target erlang -- --glacier
29 | # Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
30 | ```
31 |
32 | ### Demo for target JavaScript on NodeJS
33 |
34 | ```shell
35 | # Traditional test runs
36 | gleam test --target javascript --runtime node
37 | gleam test --target javascript --runtime node -- test/glacier_demo/glacier_demo_module_a_test.gleam
38 |
39 | # Incremental interactive test
40 | gleam test --target javascript --runtime node -- --glacier
41 | # Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
42 | ```
43 |
44 | Notice: You may omit `--runtime node` as it is currently set as the default runtime for target `javascript`.
45 |
46 | ### Demo for target JavaScript on Deno
47 |
48 | ```shell
49 | # Traditional test runs
50 | gleam test --target javascript --runtime deno
51 | gleam test --target javascript --runtime deno -- test/glacier_demo/glacier_demo_module_a_test.gleam
52 |
53 | # Incremental interactive test
54 | gleam test --target javascript --runtime deno -- --glacier
55 | # Re-save ./src/glacier_demo/glacier_demo_module_a.gleam
56 | ```
57 |
58 | #### Privileges required for Deno
59 |
60 | Do not forget to edit `gleam.toml` to add deno privileges:
61 |
62 | ```toml
63 | [javascript.deno]
64 | allow_read = ["./"]
65 | allow_net = ["deno.land"]
66 | allow_run = ["gleam"]
67 | ```
68 |
69 | ### Caveats
70 |
71 | In line with gleam module name requirements:
72 |
73 | - `./src` and `./test` source files with white spaces will not pass detection.
74 | - Behavior towards `./src` and `./test` source files with non-alphanumeric characters is not tested, and generally unsupported.
75 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Glacier · Gleam Incremental Interactive Unit Testing
2 |
3 | [](https://hex.pm/packages/glacier)
4 | [](https://hexdocs.pm/glacier/)
5 | [](https://github.com/inoas/glacier/blob/main/LICENCE)
6 |
7 | **Glacier** brings incremental interactive unit testing to
8 | [Gleam](https://gleam.run). It is meant as a drop-in replacement for
9 | [gleeunit](https://hexdocs.pm/gleeunit) and depends on a fork of it.
10 |
11 |
12 |
13 | Glacier: «A persistent body of dense ice that is constantly moving under its own weight.»
14 |
15 |
16 | ## Installation
17 |
18 | 1. Run:
19 |
20 | ```shell
21 | gleam remove gleeunit
22 | gleam add glacier --dev
23 | gleam clean
24 | ```
25 |
26 | 2. Open `YOUR_GlEAM_PROJECT/test/YOUR_GLEAM_PROJECT.gleam` and replace `import gleeunit` with
27 | `import glacier` and `gleeunit.main()` with `glacier.main()` (or alias glacier as gleeunit).
28 | 3. If you want to run on **Deno 1.40+** open `gleam.toml` and add:
29 |
30 | ```toml
31 | [javascript.deno]
32 | allow_read = ["./"]
33 | allow_net = ["deno.land"]
34 | allow_run = ["gleam"]
35 | ```
36 |
37 | ## Usage
38 |
39 | 1. Run any one of these, of which some are synonyms:
40 | - `gleam test --target erlang -- --glacier`
41 | - `gleam test --target erl -- --glacier`
42 | - `gleam test --target javascript --runtime deno -- --glacier`
43 | - `gleam test --target javascript --runtime nodejs -- --glacier`
44 | - `gleam test --target js --runtime node -- --glacier`
45 | 2. Save gleam module within your projects `src` or `test` gleam directory and
46 | watch associated tests to re-run.
47 |
48 | *Notice: On Linux [**inotify**](https://en.wikipedia.org/wiki/Inotify) must be installed.*
49 |
50 | ## Upgrading Glacier
51 |
52 | Make sure to run `gleam clean` after upgrading Glacier as a dependency.
53 |
54 | ## Improvements over gleeunit
55 |
56 | **Glacier** differs from **gleeunit** insofar, that it let's you:
57 |
58 | 1. Pass in the `glacier` flag like so: `gleam test -- --glacier`, save a gleam
59 | module and only related test modules will rerun.
60 | 2. Or pass in a specific unit test modules to rerun, for example:
61 | `gleam test -- test/my_module_test.gleam`.
62 | 3. If `gleam test` is passed without any `--`-arguments it behaves the same as
63 | **gleeunit**.
64 | 4. You can still pass in target or runtime flags, such as `--target javascript`
65 | `--runtime deno`, aka:
66 | - `gleam test --target javascript --runtime deno -- --glacier`
67 | - `gleam test --target javascript --runtime deno -- test/my_module_test.gleam`.
68 | - `gleam test --target js --runtime deno -- test/my_module_test.gleam`.
69 |
70 | *Note: `gleam test` must only be executed from the base project directory!*
71 |
72 | ### Testing against Erlang, NodeJS and Deno simultaneously
73 |
74 | Run these in 3 terminals side by side:
75 |
76 | - `gleam test -- --glacier # this implies --target erlang`
77 | - `gleam test --target js --runtime node -- --glacier`
78 | - `gleam test --target js --runtime deno -- --glacier`
79 |
80 | ## Requirements & Installation
81 |
82 | ### Requirements
83 |
84 | Requires Gleam 1.0.0 or later.
85 |
86 | #### Target specific requirements
87 |
88 | - Erlang/OTP 25 (lower may or may not run)
89 | - NodeJS 18 LTS+ (lower may or may not run)
90 | - Deno v1.30.0+ (lower does not run)
91 |
92 | #### Target Erlang
93 |
94 | Development and testing only happens on very recent stable Erlang/OTP versions, and thus may or may not run on previous versions.
95 |
96 | Depends on [fs](https://hexdocs.pm/fs/):
97 |
98 | - Linux [relies](https://github.com/synrc/fs#backends) on [**inotify**](https://en.wikipedia.org/wiki/Inotify).
99 | - Mac: Should work out of the box.
100 | - Windows: Should work out of the box.
101 |
102 | #### Target JavaScript/NodeJS/Deno
103 |
104 | Development and testing only happens on very recent NodeJS LTS versions, and thus may or may not run on previous versions.
105 |
106 | Depends on [NodeJS:`fsPromises.watch`](https://nodejs.org/api/fs.html#fspromiseswatchfilename-options) or [`Deno.watchFs`](https://deno.land/api@v1.30.0?s=Deno.watchFs):
107 |
108 | - Linux: [relies](https://nodejs.org/docs/latest-v18.x/api/fs.html#fs_caveats) on [**inotify**](https://en.wikipedia.org/wiki/Inotify).
109 | - Mac: Should work out of the box.
110 | - Windows: Should work out of the box.
111 |
112 | ## Documentation
113 |
114 | Documentation can be found at .
115 |
116 | ## Contributing to Glacier
117 |
118 | See [DEVELOPMENT.md](./DEVELOPMENT.md).
119 |
120 | ## License
121 |
122 | [Apache 2.0](./LICENCE)
123 |
--------------------------------------------------------------------------------
/deno.json:
--------------------------------------------------------------------------------
1 | {
2 | "importMap": "./import_map.json"
3 | }
4 |
--------------------------------------------------------------------------------
/deno.lock:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2",
3 | "remote": {
4 | "https://deno.land/std@0.173.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
5 | "https://deno.land/std@0.173.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
6 | "https://deno.land/std@0.173.0/async/abortable.ts": "73acfb3ed7261ce0d930dbe89e43db8d34e017b063cf0eaa7d215477bf53442e",
7 | "https://deno.land/std@0.173.0/async/deadline.ts": "b98e50d2c42399af03ad13bbb8cf59dadb9f0cd5d70648cc0c3b9202d75ab565",
8 | "https://deno.land/std@0.173.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332",
9 | "https://deno.land/std@0.173.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8",
10 | "https://deno.land/std@0.173.0/async/delay.ts": "73aa04cec034c84fc748c7be49bb15cac3dd43a57174bfdb7a4aec22c248f0dd",
11 | "https://deno.land/std@0.173.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576",
12 | "https://deno.land/std@0.173.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9",
13 | "https://deno.land/std@0.173.0/async/pool.ts": "fd082bd4aaf26445909889435a5c74334c017847842ec035739b4ae637ae8260",
14 | "https://deno.land/std@0.173.0/async/retry.ts": "5efa3ba450ac0c07a40a82e2df296287b5013755d232049efd7ea2244f15b20f",
15 | "https://deno.land/std@0.173.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757",
16 | "https://deno.land/std@0.173.0/bytes/index_of_needle.ts": "65c939607df609374c4415598fa4dad04a2f14c4d98cd15775216f0aaf597f24",
17 | "https://deno.land/std@0.173.0/collections/map_values.ts": "431b78fd770c72cc978ca7bbfa08cdc0805e69c7d2b69ad19859e093467bd46d",
18 | "https://deno.land/std@0.173.0/crypto/timing_safe_equal.ts": "8d69ab611c67fe51b6127d97fcfb4d8e7d0e1b6b4f3e0cc4ab86744c3691f965",
19 | "https://deno.land/std@0.173.0/encoding/base64.ts": "7de04c2f8aeeb41453b09b186480be90f2ff357613b988e99fabb91d2eeceba1",
20 | "https://deno.land/std@0.173.0/encoding/base64url.ts": "3f1178f6446834457b16bfde8b559c1cd3481727fe384d3385e4a9995dc2d851",
21 | "https://deno.land/std@0.173.0/flags/mod.ts": "d1cdefa18472ef69858a17df5cf7c98445ed27ac10e1460183081303b0ebc270",
22 | "https://deno.land/std@0.173.0/fmt/printf.ts": "8afc5987c9a88d345cd4de25b30126b16062635f51de9496f0270b0e03b7b10f",
23 | "https://deno.land/std@0.173.0/fs/exists.ts": "b8c8a457b71e9d7f29b9d2f87aad8dba2739cbe637e8926d6ba6e92567875f8e",
24 | "https://deno.land/std@0.173.0/node/_core.ts": "8667bf9129cdcbaac6f5c14f4def45ca954928baaa697a0ffdb0ee1556d4c644",
25 | "https://deno.land/std@0.173.0/node/_events.d.ts": "1347437fd6b084d7c9a4e16b9fe7435f00b030970086482edeeb3b179d0775af",
26 | "https://deno.land/std@0.173.0/node/_events.mjs": "d4ba4e629abe3db9f1b14659fd5c282b7da8b2b95eaf13238eee4ebb142a2448",
27 | "https://deno.land/std@0.173.0/node/_fs/_fs_access.ts": "48a722db00fd34ec567c1d03c47f6b94d07658c658eeb7d9a10c6b823ebdefbd",
28 | "https://deno.land/std@0.173.0/node/_fs/_fs_appendFile.ts": "2e5230c88804f4b5bee29efa1ba723d71a53f9b0f85d5e6372509ba12e9c00c3",
29 | "https://deno.land/std@0.173.0/node/_fs/_fs_chmod.ts": "fcba6aa4fe2d9178746b5b4ae7f42a72a971007c855988f0e26ff8f694c3c212",
30 | "https://deno.land/std@0.173.0/node/_fs/_fs_chown.ts": "6a24414772d689f8e83b6f53f134420dc25d752bd5be56cade39e92f182c9c9a",
31 | "https://deno.land/std@0.173.0/node/_fs/_fs_close.ts": "8fc5819affb69fb5708f3babce49cd673133e939cebef0665099da78a0d0be7a",
32 | "https://deno.land/std@0.173.0/node/_fs/_fs_common.ts": "21caae4ab7c07c66244446c63c50291cc553d1224d3f6a0cd7bea688c6b2a815",
33 | "https://deno.land/std@0.173.0/node/_fs/_fs_constants.ts": "22ce5f8b07fa8fd7ba37718ad85f6655954b7585d21e6d0b9d73676c16ef1b15",
34 | "https://deno.land/std@0.173.0/node/_fs/_fs_copy.ts": "9074e3a1609b9ee10ca1a2d77e94836c57190e791a0878f7e03b2f0e4e0d5dfb",
35 | "https://deno.land/std@0.173.0/node/_fs/_fs_dir.ts": "26c16ef8003772c9cd2439b448530443ea09a1508a6d808a5913576c3d11882b",
36 | "https://deno.land/std@0.173.0/node/_fs/_fs_dirent.ts": "e8c30d8059336cb6b122738c487cb46c1bcfc4c99fd6d64186f04b4e1805be34",
37 | "https://deno.land/std@0.173.0/node/_fs/_fs_exists.ts": "012e8bf6a6a9b53f9e6451db6ddabf1b883a25e6aebb8aadf8958b57efffefd0",
38 | "https://deno.land/std@0.173.0/node/_fs/_fs_fdatasync.ts": "cfe9409aed4bfe707fb497fe5be449a678b4ae454c9068f3720138ff06f7a71f",
39 | "https://deno.land/std@0.173.0/node/_fs/_fs_fstat.ts": "b15968d0f0da997960f0814e52beee35aff5e04519f007c3ac1c431829a03ac4",
40 | "https://deno.land/std@0.173.0/node/_fs/_fs_fsync.ts": "902c1d4ef9b022c61a12c5f85db3ec4e14778019697cf453822313f9eab9516b",
41 | "https://deno.land/std@0.173.0/node/_fs/_fs_ftruncate.ts": "36d76a3d6b325345ba6fbef745ec1a39d6efb4472214ede8421449296fd25711",
42 | "https://deno.land/std@0.173.0/node/_fs/_fs_futimes.ts": "75b9aaa28588d94b9d8be3c5ca4b74595cde342d644afc9c5dda1e1dcc1e604f",
43 | "https://deno.land/std@0.173.0/node/_fs/_fs_link.ts": "5cfa4f02cbedf913d90618c1bf130796bc3cdd7cd0e59cf5defb05619ae10b8a",
44 | "https://deno.land/std@0.173.0/node/_fs/_fs_lstat.ts": "da6a26b4745dbb92eda21f07992d16497a6848fe2ded6a425ade4a2418262b57",
45 | "https://deno.land/std@0.173.0/node/_fs/_fs_mkdir.ts": "94e4341f9bbc3bae9f1474e86621d48101a4a863ce51fd6b1170ef244533c494",
46 | "https://deno.land/std@0.173.0/node/_fs/_fs_mkdtemp.ts": "33658ccb449f90d69305868b718f8fe8d72a2a8e2be7136ebd69ba313fd0b4a9",
47 | "https://deno.land/std@0.173.0/node/_fs/_fs_open.ts": "9f728953c07748a54a73bb9ff0013530e33556a688a359a554d5db5b4ed30d06",
48 | "https://deno.land/std@0.173.0/node/_fs/_fs_opendir.ts": "fe65a45b92b6b970da8f3acec15920cb5669c7a19fd07afa8ebcd248ec69740b",
49 | "https://deno.land/std@0.173.0/node/_fs/_fs_read.ts": "a0223081bc460a8af5d1bb01e59a44182629bf7bff7c583031912abf20ac6b04",
50 | "https://deno.land/std@0.173.0/node/_fs/_fs_readFile.ts": "2c155de6b568a4e5d3d089e58723355fc519de2d2c9422f7dd211cda2c8f36dc",
51 | "https://deno.land/std@0.173.0/node/_fs/_fs_readdir.ts": "85f742c2ad38bebb8ba5dee72b37a966fc4b42b10382a76a60d7a2dda0a6278c",
52 | "https://deno.land/std@0.173.0/node/_fs/_fs_readlink.ts": "d5d9746c1d3c76cce0be5045dbb3bfde100406a98f1d4db8243776a2fc5619af",
53 | "https://deno.land/std@0.173.0/node/_fs/_fs_realpath.ts": "671afd8bc1b33126d56155de3827d6ec55361631eec9f4944d7f91835d897329",
54 | "https://deno.land/std@0.173.0/node/_fs/_fs_rename.ts": "2fd973c38ab5c66d806a954914a2d2b6beec55308b6da0616837ba81946bba3b",
55 | "https://deno.land/std@0.173.0/node/_fs/_fs_rm.ts": "27c01d261a3631729f9406d9dc7be263a7adf240094ba9133da511169785023b",
56 | "https://deno.land/std@0.173.0/node/_fs/_fs_rmdir.ts": "d9a35aa265670aba4a6da10cb151139bd69762ccfb88e27f266c1260c244d3ec",
57 | "https://deno.land/std@0.173.0/node/_fs/_fs_stat.ts": "bf1ca585b624f5b183ff547f02ad40b51d47247a7fd5df84f8c27376e7a7c2d5",
58 | "https://deno.land/std@0.173.0/node/_fs/_fs_symlink.ts": "89752d75dd823be7ea2c0f2ca024b14c954f7d1507360abf883245f4b700464b",
59 | "https://deno.land/std@0.173.0/node/_fs/_fs_truncate.ts": "4333d191574be1d6ab20fdee346c0dd4868e5c9c5e8ee716e3b09bf562aee698",
60 | "https://deno.land/std@0.173.0/node/_fs/_fs_unlink.ts": "6a760088a99c7465d9da3cbd67a456a6207c9764c65926ce1e0d3172aab780a2",
61 | "https://deno.land/std@0.173.0/node/_fs/_fs_utimes.ts": "c433ef58bfd20d84d0f940c17575b496dcd4706e8dc86aea777c73f667164444",
62 | "https://deno.land/std@0.173.0/node/_fs/_fs_watch.ts": "2ed05b68759e1771515efa4c6d19db9c956cfbc79a715d61e4ce8f38ac12c966",
63 | "https://deno.land/std@0.173.0/node/_fs/_fs_write.d.ts": "a405627931c1a5a3160d3f1cf028761d51b50cd632d6602cb0f98c6b39c96b23",
64 | "https://deno.land/std@0.173.0/node/_fs/_fs_write.mjs": "595abc0d7be9ef3709b62bf09972c2836b25c945f4c531a6688b910e428e1b42",
65 | "https://deno.land/std@0.173.0/node/_fs/_fs_writeFile.ts": "c65f61a167e5f80f29a88147012ade2a81233c882e51c6a07f45a153f2316a58",
66 | "https://deno.land/std@0.173.0/node/_fs/_fs_writev.d.ts": "2cd3596fe24579debe43b587d5bb5845f6f0ce3913357376eb279511ce832d15",
67 | "https://deno.land/std@0.173.0/node/_fs/_fs_writev.mjs": "54adae0d5e5148d2ee0690d04f7272dbccd1242ffbdf838778ac514c10197844",
68 | "https://deno.land/std@0.173.0/node/_global.d.ts": "2d88342f38b4083b858998e27c706725fb03a74aa14ef8d985dc18438b5188e4",
69 | "https://deno.land/std@0.173.0/node/_next_tick.ts": "9a3cf107d59b019a355d3cf32275b4c6157282e4b68ea85b46a799cb1d379305",
70 | "https://deno.land/std@0.173.0/node/_process/exiting.ts": "6e336180aaabd1192bf99ffeb0d14b689116a3dec1dfb34a2afbacd6766e98ab",
71 | "https://deno.land/std@0.173.0/node/_process/process.ts": "c96bb1f6253824c372f4866ee006dcefda02b7050d46759736e403f862d91051",
72 | "https://deno.land/std@0.173.0/node/_process/stdio.mjs": "cf17727eac8da3a665851df700b5aca6a12bacc3ebbf33e63e4b919f80ba44a6",
73 | "https://deno.land/std@0.173.0/node/_process/streams.mjs": "c1461c4dbf963a93a0ca8233467573a685bbde347562573761cc9435fd7080f6",
74 | "https://deno.land/std@0.173.0/node/_stream.d.ts": "112e1a0677cd6db932c3ce0e6e5bbdc7a2ac1874572f449044ecc82afcf5ee2e",
75 | "https://deno.land/std@0.173.0/node/_stream.mjs": "d6e2c86c1158ac65b4c2ca4fa019d7e84374ff12e21e2175345fe68c0823efe3",
76 | "https://deno.land/std@0.173.0/node/_util/_util_callbackify.ts": "a7ffe799ac5f54f3a780ee1c9b190b94dc7dc8afbb430c0e1c73756638d25d64",
77 | "https://deno.land/std@0.173.0/node/_utils.ts": "7fd55872a0cf9275e3c080a60e2fa6d45b8de9e956ebcde9053e72a344185884",
78 | "https://deno.land/std@0.173.0/node/buffer.ts": "85617be2063eccaf177dbb84c7580d1e32023724ed14bd9df4e453b152a26167",
79 | "https://deno.land/std@0.173.0/node/child_process.ts": "87ce152680caeedcfa56474e06df7294d27a65ab1c52462d8cc8f45931d68fc1",
80 | "https://deno.land/std@0.173.0/node/events.ts": "d2de352d509de11a375e2cb397d6b98f5fed4e562fc1d41be33214903a38e6b0",
81 | "https://deno.land/std@0.173.0/node/fs.ts": "de13cb511655b594157b327cd11bb833cc96051409f34148f043e8a8a92d66a1",
82 | "https://deno.land/std@0.173.0/node/fs/promises.ts": "5db686797cec9a6bc7b1460beb7e049ada81a43bbc0ff8231a26442261ec3fd0",
83 | "https://deno.land/std@0.173.0/node/internal/assert.mjs": "1d50c20eeaf16a6d9c1d90347e497669cebc915f5ee238417a73847eb4c2f0de",
84 | "https://deno.land/std@0.173.0/node/internal/buffer.d.ts": "bdfa991cd88cb02fd08bf8235d2618550e3e511c970b2a8f2e1a6885a2793cac",
85 | "https://deno.land/std@0.173.0/node/internal/buffer.mjs": "e92303a3cc6d9aaabcd270a937ad9319825d9ba08cb332650944df4562029b27",
86 | "https://deno.land/std@0.173.0/node/internal/child_process.ts": "047d7e872b2a3cd58d5cce50146a77c003d011ecb8e67a8c630b24375665e607",
87 | "https://deno.land/std@0.173.0/node/internal/crypto/_keys.ts": "8f3c3b5a141aa0331a53c205e9338655f1b3b307a08085fd6ff6dda6f7c4190b",
88 | "https://deno.land/std@0.173.0/node/internal/crypto/constants.ts": "544d605703053218499b08214f2e25cf4310651d535b7ab995891c4b7a217693",
89 | "https://deno.land/std@0.173.0/node/internal/error_codes.ts": "8495e33f448a484518d76fa3d41d34fc20fe03c14b30130ad8e936b0035d4b8b",
90 | "https://deno.land/std@0.173.0/node/internal/errors.ts": "1c699b8a3cb93174f697a348c004b1c6d576b66688eac8a48ebb78e65c720aae",
91 | "https://deno.land/std@0.173.0/node/internal/fixed_queue.ts": "62bb119afa5b5ae8fc0c7048b50502347bec82e2588017d0b250c4671d6eff8f",
92 | "https://deno.land/std@0.173.0/node/internal/fs/streams.d.ts": "23571ff9af59d86307831b80823e440953f3e57b134ca7ec6e55b60b845d38de",
93 | "https://deno.land/std@0.173.0/node/internal/fs/streams.mjs": "5de00d105009fb8cec6b6d0a6e6e6288ae40879cc64d9bf7a84852220be9fa34",
94 | "https://deno.land/std@0.173.0/node/internal/fs/utils.mjs": "64b6dc17752fa861b46a0876647336ba24efe3b5130bd1826f1f2d59b9b374ed",
95 | "https://deno.land/std@0.173.0/node/internal/hide_stack_frames.ts": "9dd1bad0a6e62a1042ce3a51eb1b1ecee2f246907bff44835f86e8f021de679a",
96 | "https://deno.land/std@0.173.0/node/internal/idna.ts": "034043ac9273eb5ba83112c926dba1777775f1eca40e021c8703cd1720bedd9f",
97 | "https://deno.land/std@0.173.0/node/internal/net.ts": "5538d31b595ac63d4b3e90393168bc65ace2f332c3317cffa2fd780070b2d86c",
98 | "https://deno.land/std@0.173.0/node/internal/normalize_encoding.mjs": "fd1d9df61c44d7196432f6e8244621468715131d18cc79cd299fc78ac549f707",
99 | "https://deno.land/std@0.173.0/node/internal/options.ts": "888f267c3fe8f18dc7b2f2fbdbe7e4a0fd3302ff3e99f5d6645601e924f3e3fb",
100 | "https://deno.land/std@0.173.0/node/internal/primordials.mjs": "a72d86b5aa55d3d50b8e916b6a59b7cc0dc5a31da8937114b4a113ad5aa08c74",
101 | "https://deno.land/std@0.173.0/node/internal/process/per_thread.mjs": "10142bbb13978c2f8f79778ad90f3a67a8ea6d8d2970f3dfc6bf2c6fff0162a2",
102 | "https://deno.land/std@0.173.0/node/internal/querystring.ts": "479f30c136555dc3b6f09af7d0de8a70c753035c1d5b57acc696722028788323",
103 | "https://deno.land/std@0.173.0/node/internal/readline/callbacks.mjs": "bdb129b140c3b21b5e08cdc3d8e43517ad818ac03f75197338d665cca1cbaed3",
104 | "https://deno.land/std@0.173.0/node/internal/readline/utils.mjs": "c3dbf3a97c01ed14052cca3848f09e2fc24818c1822ceed57c33b9f0840f3b87",
105 | "https://deno.land/std@0.173.0/node/internal/streams/destroy.mjs": "b665fc71178919a34ddeac8389d162a81b4bc693ff7dc2557fa41b3a91011967",
106 | "https://deno.land/std@0.173.0/node/internal/streams/end-of-stream.mjs": "a4fb1c2e32d58dff440d4e716e2c4daaa403b3095304a028bb428575cfeed716",
107 | "https://deno.land/std@0.173.0/node/internal/streams/utils.mjs": "f2fe2e6bdc506da24c758970890cc2a21642045b129dee618bd3827c60dd9e33",
108 | "https://deno.land/std@0.173.0/node/internal/url.ts": "7e62e16520de552c130c354d9c725a2f5e2af453ff929a2009fa66ae445bbe14",
109 | "https://deno.land/std@0.173.0/node/internal/util.mjs": "f7fe2e1ca5e66f550ad0856b9f5ee4d666f0c071fe212ea7fc7f37cfa81f97a5",
110 | "https://deno.land/std@0.173.0/node/internal/util/comparisons.ts": "0da27292b2714c14873a798221189321ee044e52a9c5df99979f8415f1348665",
111 | "https://deno.land/std@0.173.0/node/internal/util/debuglog.ts": "a2392980a65cc6916afc17fa6686242ee0e3b47bd98c792ff59358560b24185e",
112 | "https://deno.land/std@0.173.0/node/internal/util/inspect.mjs": "11d7c9cab514b8e485acc3978c74b837263ff9c08ae4537fa18ad56bae633259",
113 | "https://deno.land/std@0.173.0/node/internal/util/types.ts": "4f3625ea39111eaae1443c834e769b0c5ce9ea33b31d5a853b02af6a78105178",
114 | "https://deno.land/std@0.173.0/node/internal/validators.mjs": "e02f2b02dd072a5d623970292588d541204dc82207b4c58985d933a5f4b382e6",
115 | "https://deno.land/std@0.173.0/node/internal_binding/_libuv_winerror.ts": "30c9569603d4b97a1f1a034d88a3f74800d5ea1f12fcc3d225c9899d4e1a518b",
116 | "https://deno.land/std@0.173.0/node/internal_binding/_listen.ts": "c6038be47116f7755c01fd98340a0d1e8e66ef874710ab59ed3f5607d50d7a25",
117 | "https://deno.land/std@0.173.0/node/internal_binding/_node.ts": "cb2389b0eab121df99853eb6a5e3a684e4537e065fb8bf2cca0cbf219ce4e32e",
118 | "https://deno.land/std@0.173.0/node/internal_binding/_timingSafeEqual.ts": "7d9732464d3c669ff07713868ce5d25bc974a06112edbfb5f017fc3c70c0853e",
119 | "https://deno.land/std@0.173.0/node/internal_binding/_utils.ts": "7c58a2fbb031a204dee9583ba211cf9c67922112fe77e7f0b3226112469e9fe1",
120 | "https://deno.land/std@0.173.0/node/internal_binding/_winerror.ts": "3e8cfdfe22e89f13d2b28529bab35155e6b1730c0221ec5a6fc7077dc037be13",
121 | "https://deno.land/std@0.173.0/node/internal_binding/ares.ts": "bdd34c679265a6c115a8cfdde000656837a0a0dcdb0e4c258e622e136e9c31b8",
122 | "https://deno.land/std@0.173.0/node/internal_binding/async_wrap.ts": "0dc5ae64eea2c9e57ab17887ef1573922245167ffe38e3685c28d636f487f1b7",
123 | "https://deno.land/std@0.173.0/node/internal_binding/buffer.ts": "31729e0537921d6c730ad0afea44a7e8a0a1044d070ade8368226cb6f7390c8b",
124 | "https://deno.land/std@0.173.0/node/internal_binding/cares_wrap.ts": "9b7247772167f8ed56acd0244a232d9d50e8d7c9cfc379f77f3d54cecc2f32ab",
125 | "https://deno.land/std@0.173.0/node/internal_binding/config.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
126 | "https://deno.land/std@0.173.0/node/internal_binding/connection_wrap.ts": "7dd089ea46de38e4992d0f43a09b586e4cf04878fb06863c1cb8cb2ece7da521",
127 | "https://deno.land/std@0.173.0/node/internal_binding/constants.ts": "21ff9d1ee71d0a2086541083a7711842fc6ae25e264dbf45c73815aadce06f4c",
128 | "https://deno.land/std@0.173.0/node/internal_binding/contextify.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
129 | "https://deno.land/std@0.173.0/node/internal_binding/credentials.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
130 | "https://deno.land/std@0.173.0/node/internal_binding/crypto.ts": "29e8f94f283a2e7d4229d3551369c6a40c2af9737fad948cb9be56bef6c468cd",
131 | "https://deno.land/std@0.173.0/node/internal_binding/errors.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
132 | "https://deno.land/std@0.173.0/node/internal_binding/fs.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
133 | "https://deno.land/std@0.173.0/node/internal_binding/fs_dir.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
134 | "https://deno.land/std@0.173.0/node/internal_binding/fs_event_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
135 | "https://deno.land/std@0.173.0/node/internal_binding/handle_wrap.ts": "adf0b8063da2c54f26edd5e8ec50296a4d38e42716a70a229f14654b17a071d9",
136 | "https://deno.land/std@0.173.0/node/internal_binding/heap_utils.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
137 | "https://deno.land/std@0.173.0/node/internal_binding/http_parser.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
138 | "https://deno.land/std@0.173.0/node/internal_binding/icu.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
139 | "https://deno.land/std@0.173.0/node/internal_binding/inspector.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
140 | "https://deno.land/std@0.173.0/node/internal_binding/js_stream.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
141 | "https://deno.land/std@0.173.0/node/internal_binding/messaging.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
142 | "https://deno.land/std@0.173.0/node/internal_binding/mod.ts": "9fc65f7af1d35e2d3557539a558ea9ad7a9954eefafe614ad82d94bddfe25845",
143 | "https://deno.land/std@0.173.0/node/internal_binding/module_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
144 | "https://deno.land/std@0.173.0/node/internal_binding/native_module.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
145 | "https://deno.land/std@0.173.0/node/internal_binding/natives.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
146 | "https://deno.land/std@0.173.0/node/internal_binding/node_file.ts": "21edbbc95653e45514aff252b6cae7bf127a4338cbc5f090557d258aa205d8a5",
147 | "https://deno.land/std@0.173.0/node/internal_binding/node_options.ts": "0b5cb0bf4379a39278d7b7bb6bb2c2751baf428fe437abe5ed3e8441fae1f18b",
148 | "https://deno.land/std@0.173.0/node/internal_binding/options.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
149 | "https://deno.land/std@0.173.0/node/internal_binding/os.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
150 | "https://deno.land/std@0.173.0/node/internal_binding/performance.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
151 | "https://deno.land/std@0.173.0/node/internal_binding/pipe_wrap.ts": "e5429879551fb7195039986fe6da920a86971fad4342046cbf653643e6c85e21",
152 | "https://deno.land/std@0.173.0/node/internal_binding/process_methods.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
153 | "https://deno.land/std@0.173.0/node/internal_binding/report.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
154 | "https://deno.land/std@0.173.0/node/internal_binding/serdes.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
155 | "https://deno.land/std@0.173.0/node/internal_binding/signal_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
156 | "https://deno.land/std@0.173.0/node/internal_binding/spawn_sync.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
157 | "https://deno.land/std@0.173.0/node/internal_binding/stream_wrap.ts": "452bff74d1db280a0cd78c75a95bb6d163e849e06e9638c4af405d40296bd050",
158 | "https://deno.land/std@0.173.0/node/internal_binding/string_decoder.ts": "54c3c1cbd5a9254881be58bf22637965dc69535483014dab60487e299cb95445",
159 | "https://deno.land/std@0.173.0/node/internal_binding/symbols.ts": "4dee2f3a400d711fd57fa3430b8de1fdb011e08e260b81fef5b81cc06ed77129",
160 | "https://deno.land/std@0.173.0/node/internal_binding/task_queue.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
161 | "https://deno.land/std@0.173.0/node/internal_binding/tcp_wrap.ts": "cbede7224fcf0adc4b04e2e1222488a7a9c137807f143bd32cc8b1a121e0d4fa",
162 | "https://deno.land/std@0.173.0/node/internal_binding/timers.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
163 | "https://deno.land/std@0.173.0/node/internal_binding/tls_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
164 | "https://deno.land/std@0.173.0/node/internal_binding/trace_events.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
165 | "https://deno.land/std@0.173.0/node/internal_binding/tty_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
166 | "https://deno.land/std@0.173.0/node/internal_binding/types.ts": "5a658bf08975af30d0fad6fa6247274379be26ba3f023425bec03e61c74083ef",
167 | "https://deno.land/std@0.173.0/node/internal_binding/udp_wrap.ts": "cc86f7e51bf56fd619505cf9d4f77d7aae1526abdf295399dd277162d28ca6c1",
168 | "https://deno.land/std@0.173.0/node/internal_binding/url.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
169 | "https://deno.land/std@0.173.0/node/internal_binding/util.ts": "808ff3b92740284184ab824adfc420e75398c88c8bccf5111f0c24ac18c48f10",
170 | "https://deno.land/std@0.173.0/node/internal_binding/uv.ts": "eb0048e30af4db407fb3f95563e30d70efd6187051c033713b0a5b768593a3a3",
171 | "https://deno.land/std@0.173.0/node/internal_binding/v8.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
172 | "https://deno.land/std@0.173.0/node/internal_binding/worker.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
173 | "https://deno.land/std@0.173.0/node/internal_binding/zlib.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
174 | "https://deno.land/std@0.173.0/node/path.ts": "1c6aa9101554136525b368e8280f0f78136d4071dd71ad3a70477f27d9e4dd91",
175 | "https://deno.land/std@0.173.0/node/path/_constants.ts": "2e2f68b8679cbf0ef118de8e5719e90cfb091de17d4a7c026c911b6772e6a247",
176 | "https://deno.land/std@0.173.0/node/path/_interface.ts": "c67d76726d0f86ea62ec68d17f11d50680c4659a60a0ea6dcd2488109435b4ce",
177 | "https://deno.land/std@0.173.0/node/path/_util.ts": "44deaf5bbd947eafb3439ea7208d0625e231c5f55c421fe83f5ef91218dcd28c",
178 | "https://deno.land/std@0.173.0/node/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
179 | "https://deno.land/std@0.173.0/node/path/glob.ts": "b5fc2aed74aa7511cfd07d52dcd595cc18cd7ca431326a664e735d8905d85ce8",
180 | "https://deno.land/std@0.173.0/node/path/mod.ts": "cad27b16a7a3a8c2bb3ad1ba68a63d11e4fb616d63fd55c95e399a0a3a927be2",
181 | "https://deno.land/std@0.173.0/node/path/posix.ts": "a066e77f554358a82b4a693726faa41932f02f5bcd520f07afb6b2372e62484d",
182 | "https://deno.land/std@0.173.0/node/path/separator.ts": "5cfefe182e88bc8138022475703a9b39b13250c79bf234cdc6e3be9afd639662",
183 | "https://deno.land/std@0.173.0/node/path/win32.ts": "3a1b21948e0063cf1ac1c6834ef3ed633b5405f107be01aadfaedd2088b57eef",
184 | "https://deno.land/std@0.173.0/node/process.ts": "6608012d6d51a17a7346f36079c574b9b9f81f1b5c35436489ad089f39757466",
185 | "https://deno.land/std@0.173.0/node/querystring.ts": "2dce8068cb80ce2bf503aecd888be1b89827288352b6581e0fc401886d56cd86",
186 | "https://deno.land/std@0.173.0/node/stream.ts": "09e348302af40dcc7dc58aa5e40fdff868d11d8d6b0cfb85cbb9c75b9fe450c7",
187 | "https://deno.land/std@0.173.0/node/string_decoder.ts": "1a17e3572037c512cc5fc4b29076613e90f225474362d18da908cb7e5ccb7e88",
188 | "https://deno.land/std@0.173.0/node/url.ts": "f8c6656f32728a447705a273e3d8a5118631c0b6560d13fc613901ec9a3f69d0",
189 | "https://deno.land/std@0.173.0/node/util.ts": "4c12edeafde7e50dfe2d4022e383decb422c77858b938b093698cb7250c9e125",
190 | "https://deno.land/std@0.173.0/node/util/types.ts": "461b2e1118fd32456967e14b99f01c892dee1e94d144d6b96e9d94eb086a9574",
191 | "https://deno.land/std@0.173.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
192 | "https://deno.land/std@0.173.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
193 | "https://deno.land/std@0.173.0/path/_util.ts": "86c2375a996c1931b2f2ac71fefd5ddf0cf0e579fa4ab12d3e4c552d4223b8d8",
194 | "https://deno.land/std@0.173.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
195 | "https://deno.land/std@0.173.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
196 | "https://deno.land/std@0.173.0/path/mod.ts": "4b83694ac500d7d31b0cdafc927080a53dc0c3027eb2895790fb155082b0d232",
197 | "https://deno.land/std@0.173.0/path/posix.ts": "0874b341c2c6968ca38d323338b8b295ea1dae10fa872a768d812e2e7d634789",
198 | "https://deno.land/std@0.173.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
199 | "https://deno.land/std@0.173.0/path/win32.ts": "672942919dd66ce1b8c224e77d3447e2ad8254eaff13fe6946420a9f78fa141e",
200 | "https://deno.land/std@0.173.0/streams/write_all.ts": "3b2e1ce44913f966348ce353d02fa5369e94115181037cd8b602510853ec3033",
201 | "https://deno.land/std@0.173.0/types.d.ts": "220ed56662a0bd393ba5d124aa6ae2ad36a00d2fcbc0e8666a65f4606aaa9784",
202 | "https://deno.land/std@0.174.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
203 | "https://deno.land/std@0.174.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
204 | "https://deno.land/std@0.174.0/async/abortable.ts": "73acfb3ed7261ce0d930dbe89e43db8d34e017b063cf0eaa7d215477bf53442e",
205 | "https://deno.land/std@0.174.0/async/deadline.ts": "b98e50d2c42399af03ad13bbb8cf59dadb9f0cd5d70648cc0c3b9202d75ab565",
206 | "https://deno.land/std@0.174.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332",
207 | "https://deno.land/std@0.174.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8",
208 | "https://deno.land/std@0.174.0/async/delay.ts": "73aa04cec034c84fc748c7be49bb15cac3dd43a57174bfdb7a4aec22c248f0dd",
209 | "https://deno.land/std@0.174.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576",
210 | "https://deno.land/std@0.174.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9",
211 | "https://deno.land/std@0.174.0/async/pool.ts": "fd082bd4aaf26445909889435a5c74334c017847842ec035739b4ae637ae8260",
212 | "https://deno.land/std@0.174.0/async/retry.ts": "5efa3ba450ac0c07a40a82e2df296287b5013755d232049efd7ea2244f15b20f",
213 | "https://deno.land/std@0.174.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757",
214 | "https://deno.land/std@0.174.0/bytes/index_of_needle.ts": "65c939607df609374c4415598fa4dad04a2f14c4d98cd15775216f0aaf597f24",
215 | "https://deno.land/std@0.174.0/collections/map_values.ts": "431b78fd770c72cc978ca7bbfa08cdc0805e69c7d2b69ad19859e093467bd46d",
216 | "https://deno.land/std@0.174.0/crypto/timing_safe_equal.ts": "8d69ab611c67fe51b6127d97fcfb4d8e7d0e1b6b4f3e0cc4ab86744c3691f965",
217 | "https://deno.land/std@0.174.0/encoding/base64.ts": "7de04c2f8aeeb41453b09b186480be90f2ff357613b988e99fabb91d2eeceba1",
218 | "https://deno.land/std@0.174.0/encoding/base64url.ts": "3f1178f6446834457b16bfde8b559c1cd3481727fe384d3385e4a9995dc2d851",
219 | "https://deno.land/std@0.174.0/flags/mod.ts": "d1cdefa18472ef69858a17df5cf7c98445ed27ac10e1460183081303b0ebc270",
220 | "https://deno.land/std@0.174.0/fmt/printf.ts": "e5b426cd6ad13df5d408e9c375c025d59de30e380c5534715bd892df874ab057",
221 | "https://deno.land/std@0.174.0/fs/exists.ts": "b8c8a457b71e9d7f29b9d2f87aad8dba2739cbe637e8926d6ba6e92567875f8e",
222 | "https://deno.land/std@0.174.0/node/_core.ts": "8667bf9129cdcbaac6f5c14f4def45ca954928baaa697a0ffdb0ee1556d4c644",
223 | "https://deno.land/std@0.174.0/node/_events.d.ts": "1347437fd6b084d7c9a4e16b9fe7435f00b030970086482edeeb3b179d0775af",
224 | "https://deno.land/std@0.174.0/node/_events.mjs": "d4ba4e629abe3db9f1b14659fd5c282b7da8b2b95eaf13238eee4ebb142a2448",
225 | "https://deno.land/std@0.174.0/node/_fs/_fs_access.ts": "48a722db00fd34ec567c1d03c47f6b94d07658c658eeb7d9a10c6b823ebdefbd",
226 | "https://deno.land/std@0.174.0/node/_fs/_fs_appendFile.ts": "2e5230c88804f4b5bee29efa1ba723d71a53f9b0f85d5e6372509ba12e9c00c3",
227 | "https://deno.land/std@0.174.0/node/_fs/_fs_chmod.ts": "fcba6aa4fe2d9178746b5b4ae7f42a72a971007c855988f0e26ff8f694c3c212",
228 | "https://deno.land/std@0.174.0/node/_fs/_fs_chown.ts": "6a24414772d689f8e83b6f53f134420dc25d752bd5be56cade39e92f182c9c9a",
229 | "https://deno.land/std@0.174.0/node/_fs/_fs_close.ts": "8fc5819affb69fb5708f3babce49cd673133e939cebef0665099da78a0d0be7a",
230 | "https://deno.land/std@0.174.0/node/_fs/_fs_common.ts": "21caae4ab7c07c66244446c63c50291cc553d1224d3f6a0cd7bea688c6b2a815",
231 | "https://deno.land/std@0.174.0/node/_fs/_fs_constants.ts": "22ce5f8b07fa8fd7ba37718ad85f6655954b7585d21e6d0b9d73676c16ef1b15",
232 | "https://deno.land/std@0.174.0/node/_fs/_fs_copy.ts": "9074e3a1609b9ee10ca1a2d77e94836c57190e791a0878f7e03b2f0e4e0d5dfb",
233 | "https://deno.land/std@0.174.0/node/_fs/_fs_dir.ts": "26c16ef8003772c9cd2439b448530443ea09a1508a6d808a5913576c3d11882b",
234 | "https://deno.land/std@0.174.0/node/_fs/_fs_dirent.ts": "e8c30d8059336cb6b122738c487cb46c1bcfc4c99fd6d64186f04b4e1805be34",
235 | "https://deno.land/std@0.174.0/node/_fs/_fs_exists.ts": "012e8bf6a6a9b53f9e6451db6ddabf1b883a25e6aebb8aadf8958b57efffefd0",
236 | "https://deno.land/std@0.174.0/node/_fs/_fs_fdatasync.ts": "cfe9409aed4bfe707fb497fe5be449a678b4ae454c9068f3720138ff06f7a71f",
237 | "https://deno.land/std@0.174.0/node/_fs/_fs_fstat.ts": "b15968d0f0da997960f0814e52beee35aff5e04519f007c3ac1c431829a03ac4",
238 | "https://deno.land/std@0.174.0/node/_fs/_fs_fsync.ts": "902c1d4ef9b022c61a12c5f85db3ec4e14778019697cf453822313f9eab9516b",
239 | "https://deno.land/std@0.174.0/node/_fs/_fs_ftruncate.ts": "36d76a3d6b325345ba6fbef745ec1a39d6efb4472214ede8421449296fd25711",
240 | "https://deno.land/std@0.174.0/node/_fs/_fs_futimes.ts": "75b9aaa28588d94b9d8be3c5ca4b74595cde342d644afc9c5dda1e1dcc1e604f",
241 | "https://deno.land/std@0.174.0/node/_fs/_fs_link.ts": "5cfa4f02cbedf913d90618c1bf130796bc3cdd7cd0e59cf5defb05619ae10b8a",
242 | "https://deno.land/std@0.174.0/node/_fs/_fs_lstat.ts": "da6a26b4745dbb92eda21f07992d16497a6848fe2ded6a425ade4a2418262b57",
243 | "https://deno.land/std@0.174.0/node/_fs/_fs_mkdir.ts": "94e4341f9bbc3bae9f1474e86621d48101a4a863ce51fd6b1170ef244533c494",
244 | "https://deno.land/std@0.174.0/node/_fs/_fs_mkdtemp.ts": "33658ccb449f90d69305868b718f8fe8d72a2a8e2be7136ebd69ba313fd0b4a9",
245 | "https://deno.land/std@0.174.0/node/_fs/_fs_open.ts": "9f728953c07748a54a73bb9ff0013530e33556a688a359a554d5db5b4ed30d06",
246 | "https://deno.land/std@0.174.0/node/_fs/_fs_opendir.ts": "fe65a45b92b6b970da8f3acec15920cb5669c7a19fd07afa8ebcd248ec69740b",
247 | "https://deno.land/std@0.174.0/node/_fs/_fs_read.ts": "a0223081bc460a8af5d1bb01e59a44182629bf7bff7c583031912abf20ac6b04",
248 | "https://deno.land/std@0.174.0/node/_fs/_fs_readFile.ts": "2c155de6b568a4e5d3d089e58723355fc519de2d2c9422f7dd211cda2c8f36dc",
249 | "https://deno.land/std@0.174.0/node/_fs/_fs_readdir.ts": "85f742c2ad38bebb8ba5dee72b37a966fc4b42b10382a76a60d7a2dda0a6278c",
250 | "https://deno.land/std@0.174.0/node/_fs/_fs_readlink.ts": "d5d9746c1d3c76cce0be5045dbb3bfde100406a98f1d4db8243776a2fc5619af",
251 | "https://deno.land/std@0.174.0/node/_fs/_fs_realpath.ts": "671afd8bc1b33126d56155de3827d6ec55361631eec9f4944d7f91835d897329",
252 | "https://deno.land/std@0.174.0/node/_fs/_fs_rename.ts": "2fd973c38ab5c66d806a954914a2d2b6beec55308b6da0616837ba81946bba3b",
253 | "https://deno.land/std@0.174.0/node/_fs/_fs_rm.ts": "27c01d261a3631729f9406d9dc7be263a7adf240094ba9133da511169785023b",
254 | "https://deno.land/std@0.174.0/node/_fs/_fs_rmdir.ts": "d9a35aa265670aba4a6da10cb151139bd69762ccfb88e27f266c1260c244d3ec",
255 | "https://deno.land/std@0.174.0/node/_fs/_fs_stat.ts": "bf1ca585b624f5b183ff547f02ad40b51d47247a7fd5df84f8c27376e7a7c2d5",
256 | "https://deno.land/std@0.174.0/node/_fs/_fs_symlink.ts": "89752d75dd823be7ea2c0f2ca024b14c954f7d1507360abf883245f4b700464b",
257 | "https://deno.land/std@0.174.0/node/_fs/_fs_truncate.ts": "4333d191574be1d6ab20fdee346c0dd4868e5c9c5e8ee716e3b09bf562aee698",
258 | "https://deno.land/std@0.174.0/node/_fs/_fs_unlink.ts": "6a760088a99c7465d9da3cbd67a456a6207c9764c65926ce1e0d3172aab780a2",
259 | "https://deno.land/std@0.174.0/node/_fs/_fs_utimes.ts": "c433ef58bfd20d84d0f940c17575b496dcd4706e8dc86aea777c73f667164444",
260 | "https://deno.land/std@0.174.0/node/_fs/_fs_watch.ts": "2ed05b68759e1771515efa4c6d19db9c956cfbc79a715d61e4ce8f38ac12c966",
261 | "https://deno.land/std@0.174.0/node/_fs/_fs_write.d.ts": "a405627931c1a5a3160d3f1cf028761d51b50cd632d6602cb0f98c6b39c96b23",
262 | "https://deno.land/std@0.174.0/node/_fs/_fs_write.mjs": "595abc0d7be9ef3709b62bf09972c2836b25c945f4c531a6688b910e428e1b42",
263 | "https://deno.land/std@0.174.0/node/_fs/_fs_writeFile.ts": "c65f61a167e5f80f29a88147012ade2a81233c882e51c6a07f45a153f2316a58",
264 | "https://deno.land/std@0.174.0/node/_fs/_fs_writev.d.ts": "2cd3596fe24579debe43b587d5bb5845f6f0ce3913357376eb279511ce832d15",
265 | "https://deno.land/std@0.174.0/node/_fs/_fs_writev.mjs": "54adae0d5e5148d2ee0690d04f7272dbccd1242ffbdf838778ac514c10197844",
266 | "https://deno.land/std@0.174.0/node/_global.d.ts": "2d88342f38b4083b858998e27c706725fb03a74aa14ef8d985dc18438b5188e4",
267 | "https://deno.land/std@0.174.0/node/_next_tick.ts": "9a3cf107d59b019a355d3cf32275b4c6157282e4b68ea85b46a799cb1d379305",
268 | "https://deno.land/std@0.174.0/node/_process/exiting.ts": "6e336180aaabd1192bf99ffeb0d14b689116a3dec1dfb34a2afbacd6766e98ab",
269 | "https://deno.land/std@0.174.0/node/_process/process.ts": "c96bb1f6253824c372f4866ee006dcefda02b7050d46759736e403f862d91051",
270 | "https://deno.land/std@0.174.0/node/_process/stdio.mjs": "cf17727eac8da3a665851df700b5aca6a12bacc3ebbf33e63e4b919f80ba44a6",
271 | "https://deno.land/std@0.174.0/node/_process/streams.mjs": "c1461c4dbf963a93a0ca8233467573a685bbde347562573761cc9435fd7080f6",
272 | "https://deno.land/std@0.174.0/node/_stream.d.ts": "112e1a0677cd6db932c3ce0e6e5bbdc7a2ac1874572f449044ecc82afcf5ee2e",
273 | "https://deno.land/std@0.174.0/node/_stream.mjs": "d6e2c86c1158ac65b4c2ca4fa019d7e84374ff12e21e2175345fe68c0823efe3",
274 | "https://deno.land/std@0.174.0/node/_util/_util_callbackify.ts": "a7ffe799ac5f54f3a780ee1c9b190b94dc7dc8afbb430c0e1c73756638d25d64",
275 | "https://deno.land/std@0.174.0/node/_utils.ts": "7fd55872a0cf9275e3c080a60e2fa6d45b8de9e956ebcde9053e72a344185884",
276 | "https://deno.land/std@0.174.0/node/buffer.ts": "85617be2063eccaf177dbb84c7580d1e32023724ed14bd9df4e453b152a26167",
277 | "https://deno.land/std@0.174.0/node/child_process.ts": "63e235b64473038d869076034884cbe4e6bd0a39cf28ebaadf883bcfb5af4457",
278 | "https://deno.land/std@0.174.0/node/events.ts": "d2de352d509de11a375e2cb397d6b98f5fed4e562fc1d41be33214903a38e6b0",
279 | "https://deno.land/std@0.174.0/node/fs.ts": "de13cb511655b594157b327cd11bb833cc96051409f34148f043e8a8a92d66a1",
280 | "https://deno.land/std@0.174.0/node/fs/promises.ts": "5db686797cec9a6bc7b1460beb7e049ada81a43bbc0ff8231a26442261ec3fd0",
281 | "https://deno.land/std@0.174.0/node/internal/assert.mjs": "1d50c20eeaf16a6d9c1d90347e497669cebc915f5ee238417a73847eb4c2f0de",
282 | "https://deno.land/std@0.174.0/node/internal/buffer.d.ts": "bdfa991cd88cb02fd08bf8235d2618550e3e511c970b2a8f2e1a6885a2793cac",
283 | "https://deno.land/std@0.174.0/node/internal/buffer.mjs": "e92303a3cc6d9aaabcd270a937ad9319825d9ba08cb332650944df4562029b27",
284 | "https://deno.land/std@0.174.0/node/internal/child_process.ts": "047d7e872b2a3cd58d5cce50146a77c003d011ecb8e67a8c630b24375665e607",
285 | "https://deno.land/std@0.174.0/node/internal/crypto/_keys.ts": "8f3c3b5a141aa0331a53c205e9338655f1b3b307a08085fd6ff6dda6f7c4190b",
286 | "https://deno.land/std@0.174.0/node/internal/crypto/constants.ts": "544d605703053218499b08214f2e25cf4310651d535b7ab995891c4b7a217693",
287 | "https://deno.land/std@0.174.0/node/internal/error_codes.ts": "8495e33f448a484518d76fa3d41d34fc20fe03c14b30130ad8e936b0035d4b8b",
288 | "https://deno.land/std@0.174.0/node/internal/errors.ts": "1c699b8a3cb93174f697a348c004b1c6d576b66688eac8a48ebb78e65c720aae",
289 | "https://deno.land/std@0.174.0/node/internal/fixed_queue.ts": "62bb119afa5b5ae8fc0c7048b50502347bec82e2588017d0b250c4671d6eff8f",
290 | "https://deno.land/std@0.174.0/node/internal/fs/streams.d.ts": "23571ff9af59d86307831b80823e440953f3e57b134ca7ec6e55b60b845d38de",
291 | "https://deno.land/std@0.174.0/node/internal/fs/streams.mjs": "5de00d105009fb8cec6b6d0a6e6e6288ae40879cc64d9bf7a84852220be9fa34",
292 | "https://deno.land/std@0.174.0/node/internal/fs/utils.mjs": "64b6dc17752fa861b46a0876647336ba24efe3b5130bd1826f1f2d59b9b374ed",
293 | "https://deno.land/std@0.174.0/node/internal/hide_stack_frames.ts": "9dd1bad0a6e62a1042ce3a51eb1b1ecee2f246907bff44835f86e8f021de679a",
294 | "https://deno.land/std@0.174.0/node/internal/idna.ts": "034043ac9273eb5ba83112c926dba1777775f1eca40e021c8703cd1720bedd9f",
295 | "https://deno.land/std@0.174.0/node/internal/net.ts": "5538d31b595ac63d4b3e90393168bc65ace2f332c3317cffa2fd780070b2d86c",
296 | "https://deno.land/std@0.174.0/node/internal/normalize_encoding.mjs": "fd1d9df61c44d7196432f6e8244621468715131d18cc79cd299fc78ac549f707",
297 | "https://deno.land/std@0.174.0/node/internal/options.ts": "888f267c3fe8f18dc7b2f2fbdbe7e4a0fd3302ff3e99f5d6645601e924f3e3fb",
298 | "https://deno.land/std@0.174.0/node/internal/primordials.mjs": "a72d86b5aa55d3d50b8e916b6a59b7cc0dc5a31da8937114b4a113ad5aa08c74",
299 | "https://deno.land/std@0.174.0/node/internal/process/per_thread.mjs": "10142bbb13978c2f8f79778ad90f3a67a8ea6d8d2970f3dfc6bf2c6fff0162a2",
300 | "https://deno.land/std@0.174.0/node/internal/querystring.ts": "479f30c136555dc3b6f09af7d0de8a70c753035c1d5b57acc696722028788323",
301 | "https://deno.land/std@0.174.0/node/internal/readline/callbacks.mjs": "bdb129b140c3b21b5e08cdc3d8e43517ad818ac03f75197338d665cca1cbaed3",
302 | "https://deno.land/std@0.174.0/node/internal/readline/utils.mjs": "c3dbf3a97c01ed14052cca3848f09e2fc24818c1822ceed57c33b9f0840f3b87",
303 | "https://deno.land/std@0.174.0/node/internal/streams/destroy.mjs": "b665fc71178919a34ddeac8389d162a81b4bc693ff7dc2557fa41b3a91011967",
304 | "https://deno.land/std@0.174.0/node/internal/streams/end-of-stream.mjs": "a4fb1c2e32d58dff440d4e716e2c4daaa403b3095304a028bb428575cfeed716",
305 | "https://deno.land/std@0.174.0/node/internal/streams/utils.mjs": "f2fe2e6bdc506da24c758970890cc2a21642045b129dee618bd3827c60dd9e33",
306 | "https://deno.land/std@0.174.0/node/internal/url.ts": "7e62e16520de552c130c354d9c725a2f5e2af453ff929a2009fa66ae445bbe14",
307 | "https://deno.land/std@0.174.0/node/internal/util.mjs": "f7fe2e1ca5e66f550ad0856b9f5ee4d666f0c071fe212ea7fc7f37cfa81f97a5",
308 | "https://deno.land/std@0.174.0/node/internal/util/comparisons.ts": "0da27292b2714c14873a798221189321ee044e52a9c5df99979f8415f1348665",
309 | "https://deno.land/std@0.174.0/node/internal/util/debuglog.ts": "a2392980a65cc6916afc17fa6686242ee0e3b47bd98c792ff59358560b24185e",
310 | "https://deno.land/std@0.174.0/node/internal/util/inspect.mjs": "11d7c9cab514b8e485acc3978c74b837263ff9c08ae4537fa18ad56bae633259",
311 | "https://deno.land/std@0.174.0/node/internal/util/types.ts": "4f3625ea39111eaae1443c834e769b0c5ce9ea33b31d5a853b02af6a78105178",
312 | "https://deno.land/std@0.174.0/node/internal/validators.mjs": "e02f2b02dd072a5d623970292588d541204dc82207b4c58985d933a5f4b382e6",
313 | "https://deno.land/std@0.174.0/node/internal_binding/_libuv_winerror.ts": "30c9569603d4b97a1f1a034d88a3f74800d5ea1f12fcc3d225c9899d4e1a518b",
314 | "https://deno.land/std@0.174.0/node/internal_binding/_listen.ts": "c6038be47116f7755c01fd98340a0d1e8e66ef874710ab59ed3f5607d50d7a25",
315 | "https://deno.land/std@0.174.0/node/internal_binding/_node.ts": "cb2389b0eab121df99853eb6a5e3a684e4537e065fb8bf2cca0cbf219ce4e32e",
316 | "https://deno.land/std@0.174.0/node/internal_binding/_timingSafeEqual.ts": "7d9732464d3c669ff07713868ce5d25bc974a06112edbfb5f017fc3c70c0853e",
317 | "https://deno.land/std@0.174.0/node/internal_binding/_utils.ts": "7c58a2fbb031a204dee9583ba211cf9c67922112fe77e7f0b3226112469e9fe1",
318 | "https://deno.land/std@0.174.0/node/internal_binding/_winerror.ts": "3e8cfdfe22e89f13d2b28529bab35155e6b1730c0221ec5a6fc7077dc037be13",
319 | "https://deno.land/std@0.174.0/node/internal_binding/ares.ts": "bdd34c679265a6c115a8cfdde000656837a0a0dcdb0e4c258e622e136e9c31b8",
320 | "https://deno.land/std@0.174.0/node/internal_binding/async_wrap.ts": "0dc5ae64eea2c9e57ab17887ef1573922245167ffe38e3685c28d636f487f1b7",
321 | "https://deno.land/std@0.174.0/node/internal_binding/buffer.ts": "31729e0537921d6c730ad0afea44a7e8a0a1044d070ade8368226cb6f7390c8b",
322 | "https://deno.land/std@0.174.0/node/internal_binding/cares_wrap.ts": "9b7247772167f8ed56acd0244a232d9d50e8d7c9cfc379f77f3d54cecc2f32ab",
323 | "https://deno.land/std@0.174.0/node/internal_binding/config.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
324 | "https://deno.land/std@0.174.0/node/internal_binding/connection_wrap.ts": "7dd089ea46de38e4992d0f43a09b586e4cf04878fb06863c1cb8cb2ece7da521",
325 | "https://deno.land/std@0.174.0/node/internal_binding/constants.ts": "21ff9d1ee71d0a2086541083a7711842fc6ae25e264dbf45c73815aadce06f4c",
326 | "https://deno.land/std@0.174.0/node/internal_binding/contextify.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
327 | "https://deno.land/std@0.174.0/node/internal_binding/credentials.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
328 | "https://deno.land/std@0.174.0/node/internal_binding/crypto.ts": "29e8f94f283a2e7d4229d3551369c6a40c2af9737fad948cb9be56bef6c468cd",
329 | "https://deno.land/std@0.174.0/node/internal_binding/errors.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
330 | "https://deno.land/std@0.174.0/node/internal_binding/fs.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
331 | "https://deno.land/std@0.174.0/node/internal_binding/fs_dir.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
332 | "https://deno.land/std@0.174.0/node/internal_binding/fs_event_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
333 | "https://deno.land/std@0.174.0/node/internal_binding/handle_wrap.ts": "adf0b8063da2c54f26edd5e8ec50296a4d38e42716a70a229f14654b17a071d9",
334 | "https://deno.land/std@0.174.0/node/internal_binding/heap_utils.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
335 | "https://deno.land/std@0.174.0/node/internal_binding/http_parser.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
336 | "https://deno.land/std@0.174.0/node/internal_binding/icu.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
337 | "https://deno.land/std@0.174.0/node/internal_binding/inspector.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
338 | "https://deno.land/std@0.174.0/node/internal_binding/js_stream.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
339 | "https://deno.land/std@0.174.0/node/internal_binding/messaging.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
340 | "https://deno.land/std@0.174.0/node/internal_binding/mod.ts": "9fc65f7af1d35e2d3557539a558ea9ad7a9954eefafe614ad82d94bddfe25845",
341 | "https://deno.land/std@0.174.0/node/internal_binding/module_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
342 | "https://deno.land/std@0.174.0/node/internal_binding/native_module.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
343 | "https://deno.land/std@0.174.0/node/internal_binding/natives.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
344 | "https://deno.land/std@0.174.0/node/internal_binding/node_file.ts": "21edbbc95653e45514aff252b6cae7bf127a4338cbc5f090557d258aa205d8a5",
345 | "https://deno.land/std@0.174.0/node/internal_binding/node_options.ts": "0b5cb0bf4379a39278d7b7bb6bb2c2751baf428fe437abe5ed3e8441fae1f18b",
346 | "https://deno.land/std@0.174.0/node/internal_binding/options.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
347 | "https://deno.land/std@0.174.0/node/internal_binding/os.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
348 | "https://deno.land/std@0.174.0/node/internal_binding/performance.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
349 | "https://deno.land/std@0.174.0/node/internal_binding/pipe_wrap.ts": "e5429879551fb7195039986fe6da920a86971fad4342046cbf653643e6c85e21",
350 | "https://deno.land/std@0.174.0/node/internal_binding/process_methods.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
351 | "https://deno.land/std@0.174.0/node/internal_binding/report.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
352 | "https://deno.land/std@0.174.0/node/internal_binding/serdes.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
353 | "https://deno.land/std@0.174.0/node/internal_binding/signal_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
354 | "https://deno.land/std@0.174.0/node/internal_binding/spawn_sync.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
355 | "https://deno.land/std@0.174.0/node/internal_binding/stream_wrap.ts": "452bff74d1db280a0cd78c75a95bb6d163e849e06e9638c4af405d40296bd050",
356 | "https://deno.land/std@0.174.0/node/internal_binding/string_decoder.ts": "54c3c1cbd5a9254881be58bf22637965dc69535483014dab60487e299cb95445",
357 | "https://deno.land/std@0.174.0/node/internal_binding/symbols.ts": "4dee2f3a400d711fd57fa3430b8de1fdb011e08e260b81fef5b81cc06ed77129",
358 | "https://deno.land/std@0.174.0/node/internal_binding/task_queue.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
359 | "https://deno.land/std@0.174.0/node/internal_binding/tcp_wrap.ts": "cbede7224fcf0adc4b04e2e1222488a7a9c137807f143bd32cc8b1a121e0d4fa",
360 | "https://deno.land/std@0.174.0/node/internal_binding/timers.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
361 | "https://deno.land/std@0.174.0/node/internal_binding/tls_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
362 | "https://deno.land/std@0.174.0/node/internal_binding/trace_events.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
363 | "https://deno.land/std@0.174.0/node/internal_binding/tty_wrap.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
364 | "https://deno.land/std@0.174.0/node/internal_binding/types.ts": "5a658bf08975af30d0fad6fa6247274379be26ba3f023425bec03e61c74083ef",
365 | "https://deno.land/std@0.174.0/node/internal_binding/udp_wrap.ts": "cc86f7e51bf56fd619505cf9d4f77d7aae1526abdf295399dd277162d28ca6c1",
366 | "https://deno.land/std@0.174.0/node/internal_binding/url.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
367 | "https://deno.land/std@0.174.0/node/internal_binding/util.ts": "808ff3b92740284184ab824adfc420e75398c88c8bccf5111f0c24ac18c48f10",
368 | "https://deno.land/std@0.174.0/node/internal_binding/uv.ts": "eb0048e30af4db407fb3f95563e30d70efd6187051c033713b0a5b768593a3a3",
369 | "https://deno.land/std@0.174.0/node/internal_binding/v8.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
370 | "https://deno.land/std@0.174.0/node/internal_binding/worker.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
371 | "https://deno.land/std@0.174.0/node/internal_binding/zlib.ts": "37d293009d1718205bf28e878e54a9f1ca24c1c320cee2416c20dc054104c6ea",
372 | "https://deno.land/std@0.174.0/node/path.ts": "1c6aa9101554136525b368e8280f0f78136d4071dd71ad3a70477f27d9e4dd91",
373 | "https://deno.land/std@0.174.0/node/path/_constants.ts": "2e2f68b8679cbf0ef118de8e5719e90cfb091de17d4a7c026c911b6772e6a247",
374 | "https://deno.land/std@0.174.0/node/path/_interface.ts": "c67d76726d0f86ea62ec68d17f11d50680c4659a60a0ea6dcd2488109435b4ce",
375 | "https://deno.land/std@0.174.0/node/path/_util.ts": "44deaf5bbd947eafb3439ea7208d0625e231c5f55c421fe83f5ef91218dcd28c",
376 | "https://deno.land/std@0.174.0/node/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
377 | "https://deno.land/std@0.174.0/node/path/glob.ts": "b5fc2aed74aa7511cfd07d52dcd595cc18cd7ca431326a664e735d8905d85ce8",
378 | "https://deno.land/std@0.174.0/node/path/mod.ts": "cad27b16a7a3a8c2bb3ad1ba68a63d11e4fb616d63fd55c95e399a0a3a927be2",
379 | "https://deno.land/std@0.174.0/node/path/posix.ts": "a066e77f554358a82b4a693726faa41932f02f5bcd520f07afb6b2372e62484d",
380 | "https://deno.land/std@0.174.0/node/path/separator.ts": "5cfefe182e88bc8138022475703a9b39b13250c79bf234cdc6e3be9afd639662",
381 | "https://deno.land/std@0.174.0/node/path/win32.ts": "3a1b21948e0063cf1ac1c6834ef3ed633b5405f107be01aadfaedd2088b57eef",
382 | "https://deno.land/std@0.174.0/node/process.ts": "6608012d6d51a17a7346f36079c574b9b9f81f1b5c35436489ad089f39757466",
383 | "https://deno.land/std@0.174.0/node/querystring.ts": "2dce8068cb80ce2bf503aecd888be1b89827288352b6581e0fc401886d56cd86",
384 | "https://deno.land/std@0.174.0/node/stream.ts": "09e348302af40dcc7dc58aa5e40fdff868d11d8d6b0cfb85cbb9c75b9fe450c7",
385 | "https://deno.land/std@0.174.0/node/string_decoder.ts": "1a17e3572037c512cc5fc4b29076613e90f225474362d18da908cb7e5ccb7e88",
386 | "https://deno.land/std@0.174.0/node/url.ts": "f8c6656f32728a447705a273e3d8a5118631c0b6560d13fc613901ec9a3f69d0",
387 | "https://deno.land/std@0.174.0/node/util.ts": "4c12edeafde7e50dfe2d4022e383decb422c77858b938b093698cb7250c9e125",
388 | "https://deno.land/std@0.174.0/node/util/types.ts": "461b2e1118fd32456967e14b99f01c892dee1e94d144d6b96e9d94eb086a9574",
389 | "https://deno.land/std@0.174.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
390 | "https://deno.land/std@0.174.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
391 | "https://deno.land/std@0.174.0/path/_util.ts": "86c2375a996c1931b2f2ac71fefd5ddf0cf0e579fa4ab12d3e4c552d4223b8d8",
392 | "https://deno.land/std@0.174.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
393 | "https://deno.land/std@0.174.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
394 | "https://deno.land/std@0.174.0/path/mod.ts": "4b83694ac500d7d31b0cdafc927080a53dc0c3027eb2895790fb155082b0d232",
395 | "https://deno.land/std@0.174.0/path/posix.ts": "0874b341c2c6968ca38d323338b8b295ea1dae10fa872a768d812e2e7d634789",
396 | "https://deno.land/std@0.174.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
397 | "https://deno.land/std@0.174.0/path/win32.ts": "672942919dd66ce1b8c224e77d3447e2ad8254eaff13fe6946420a9f78fa141e",
398 | "https://deno.land/std@0.174.0/streams/write_all.ts": "3b2e1ce44913f966348ce353d02fa5369e94115181037cd8b602510853ec3033",
399 | "https://deno.land/std@0.174.0/types.d.ts": "220ed56662a0bd393ba5d124aa6ae2ad36a00d2fcbc0e8666a65f4606aaa9784"
400 | }
401 | }
--------------------------------------------------------------------------------
/glacier-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inoas/glacier/aae146deb6b380ac905ed732dce97612a1b14530/glacier-logo.png
--------------------------------------------------------------------------------
/gleam.toml:
--------------------------------------------------------------------------------
1 | name = "glacier"
2 | version = "1.3.2"
3 | gleam = ">= 1.9.1"
4 |
5 | licences = ["Apache-2.0"]
6 | description = "Glacier brings incremental interactive unit testing to Gleam. It is meant as a drop-in replacement for Gleeunit and depends on and wraps a fork of it."
7 | repository = { type = "github", user = "inoas", repo = "glacier" }
8 | # links = [{ title = "Website", href = "https://gleam.run" }]
9 | internal_modules = ["glacier_demo", "glacier_demo/*"]
10 |
11 | [dependencies]
12 | argv = ">= 1.0.0 and < 2.0.0"
13 | fs = ">= 8.6.0 and < 9.0.0"
14 | glacier_gleeunit = ">= 1.3.1002 and < 2.0.0"
15 | # glacier_gleeunit = { path = "../gleeunit" }
16 | gleam_community_ansi = ">= 1.4.0 and < 2.0.0"
17 | gleam_community_colour = ">= 2.0.0 and < 3.0.0"
18 | gleam_stdlib = ">= 0.42.0 and < 2.0.0"
19 | shellout = ">= 1.6.0 and < 2.0.0"
20 | simplifile = ">= 2.0.0 and < 3.0.0"
21 |
22 | [javascript.deno]
23 | allow_net = ["deno.land"]
24 | allow_read = ["./"]
25 | allow_run = ["gleam"]
26 |
27 | [documentation]
28 | pages = [
29 | { title = "Changelog", path = "changelog.html", source = "CHANGELOG.md" },
30 | { title = "Contribute", path = "contribute.html", source = "CONTRIBUTE.md" },
31 | { title = "Licence", path = "licence.html", source = "LICENCE" },
32 | ]
33 |
--------------------------------------------------------------------------------
/import_map.json:
--------------------------------------------------------------------------------
1 | {
2 | "imports": {
3 | "child_process": "https://deno.land/std@0.173.0/node/child_process.ts",
4 | "fs" : "https://deno.land/std@0.173.0/node/fs.ts",
5 | "fs/promises": "https://deno.land/std@0.173.0/node/fs/promises.ts",
6 | "path" : "https://deno.land/std@0.173.0/node/path.ts",
7 | "process": "https://deno.land/std@0.173.0/node/process.ts"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/manifest.toml:
--------------------------------------------------------------------------------
1 | # This file was generated by Gleam
2 | # You typically do not need to edit this file
3 |
4 | packages = [
5 | { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
6 | { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" },
7 | { name = "fs", version = "8.6.1", build_tools = ["rebar3"], requirements = [], otp_app = "fs", source = "hex", outer_checksum = "61EA2BDAEDAE4E2024D0D25C63E44DCCF65622D4402DB4A2DF12868D1546503F" },
8 | { name = "glacier_gleeunit", version = "1.3.1002", build_tools = ["gleam"], requirements = ["argv", "gleam_stdlib"], otp_app = "glacier_gleeunit", source = "hex", outer_checksum = "D33D329C6CD57562D0697E6D686918D6140A5C881FD2C16A9A3905DAC0EB573F" },
9 | { name = "gleam_community_ansi", version = "1.4.3", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "8A62AE9CC6EA65BEA630D95016D6C07E4F9973565FA3D0DE68DC4200D8E0DD27" },
10 | { name = "gleam_community_colour", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "FDD6AC62C6EC8506C005949A4FCEF032038191D5EAAEC3C9A203CD53AE956ACA" },
11 | { name = "gleam_json", version = "2.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "C55C5C2B318533A8072D221C5E06E5A75711C129E420DD1CE463342106012E5D" },
12 | { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" },
13 | { name = "gleam_stdlib", version = "0.59.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "F8FEE9B35797301994B81AF75508CF87C328FE1585558B0FFD188DC2B32EAA95" },
14 | { name = "shellout", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "1BDC03438FEB97A6AF3E396F4ABEB32BECF20DF2452EC9A8C0ACEB7BDDF70B14" },
15 | { name = "simplifile", version = "2.2.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "C88E0EE2D509F6D86EB55161D631657675AA7684DAB83822F7E59EB93D9A60E3" },
16 | ]
17 |
18 | [requirements]
19 | argv = { version = ">= 1.0.0 and < 2.0.0" }
20 | fs = { version = ">= 8.6.0 and < 9.0.0" }
21 | glacier_gleeunit = { version = ">= 1.3.1002 and < 2.0.0" }
22 | gleam_community_ansi = { version = ">= 1.4.0 and < 2.0.0" }
23 | gleam_community_colour = { version = ">= 2.0.0 and < 3.0.0" }
24 | gleam_stdlib = { version = ">= 0.42.0 and < 2.0.0" }
25 | shellout = { version = ">= 1.6.0 and < 2.0.0" }
26 | simplifile = { version = ">= 2.0.0 and < 3.0.0" }
27 |
--------------------------------------------------------------------------------
/src/glacier.gleam:
--------------------------------------------------------------------------------
1 | import gleam/io
2 | import gleam/list
3 | import gleam/string
4 | import gleam/string_tree
5 | import gleam_community/ansi
6 | import gleam_community/colour
7 | import gleeunit
8 |
9 | /// Lets Gleam switch code based on the current target.
10 | ///
11 | type Target {
12 | ErlangTarget
13 | JavaScriptTarget
14 | }
15 |
16 | /// Atom to internally differentiate between `src` and `test` modules.
17 | /// Public because the FFIs rely on it.
18 | ///
19 | pub type ModuleKind {
20 | SrcModuleKind
21 | TestModuleKind
22 | }
23 |
24 | fn light_cyan_ansi_colour() {
25 | let assert Ok(colour) = colour.from_rgb255(r: 156, g: 231, b: 255)
26 | colour
27 | }
28 |
29 | /// Runs either `glacier` or `gleeunit` bundled as `gleeunit`, depending on
30 | /// the given command line arguments.
31 | ///
32 | pub fn main() {
33 | let start_args = start_args()
34 | let is_incremental = list.contains(start_args, "--glacier")
35 | let is_empty_args = start_args == []
36 | case is_empty_args, is_incremental {
37 | True, _ -> gleeunit.main()
38 | _, True -> {
39 | "🏔 Glacier is watching for changes…"
40 | |> ansi.colour(light_cyan_ansi_colour())
41 | |> ansi.italic()
42 | |> io.println
43 | start_file_change_watcher(fn(modules: List(#(ModuleKind, String))) -> Nil {
44 | execute_tests(modules)
45 | })
46 | }
47 | _, _ -> gleeunit.run(start_args, halts_on_error: False)
48 | }
49 | }
50 |
51 | /// Executes tests
52 | ///
53 | fn execute_tests(modules: List(#(ModuleKind, String))) {
54 | let test_modules =
55 | list.fold(
56 | over: modules,
57 | from: [],
58 | with: fn(test_modules_acc: List(String), module: #(ModuleKind, String)) {
59 | let module_kind = module.0
60 | let full_module_path = module.1
61 | let test_modules = case module_kind {
62 | SrcModuleKind ->
63 | detect_distinct_import_module_dependency_chain(
64 | [file_name_to_module_name(full_module_path, SrcModuleKind)],
65 | [],
66 | )
67 | |> derive_test_modules_from_src_import_dependencies()
68 | |> list.map(fn(test_module) { "test/" <> test_module <> ".gleam" })
69 | TestModuleKind -> [to_relative_path(full_module_path)]
70 | }
71 | list.append(test_modules_acc, test_modules)
72 | },
73 | )
74 | |> list.unique()
75 |
76 | case test_modules {
77 | [] -> {
78 | "🏔 Did not detect any matching test modules!"
79 | |> ansi.colour(light_cyan_ansi_colour())
80 | |> ansi.bold()
81 | |> io.println
82 | Nil
83 | }
84 | test_modules -> {
85 | list.map(test_modules, with: fn(test_module: String) {
86 | "🏔 " <> test_module
87 | })
88 | |> string.join("\n")
89 | |> ansi.colour(light_cyan_ansi_colour())
90 | |> io.println
91 |
92 | let args = [
93 | "test",
94 | "--target",
95 | case target() {
96 | ErlangTarget -> "erlang"
97 | JavaScriptTarget -> "javascript"
98 | },
99 | "--",
100 | ..test_modules
101 | ]
102 |
103 | shell_exec_print(args)
104 | }
105 | }
106 | }
107 |
108 | /// Starts the file watcher.
109 | ///
110 | fn start_file_change_watcher(
111 | file_change_handler: fn(List(#(ModuleKind, String))) -> Nil,
112 | ) -> Nil {
113 | do_start_file_change_watcher(file_change_handler)
114 | Nil
115 | }
116 |
117 | /// Detects distinct import module dependency chain.
118 | ///
119 | fn detect_distinct_import_module_dependency_chain(
120 | module_names: List(String),
121 | processed_module_names: List(String),
122 | ) -> List(String) {
123 | case module_names {
124 | [] -> processed_module_names
125 | [module_name, ..rest_module_names] ->
126 | case list.contains(processed_module_names, module_name) {
127 | True ->
128 | detect_distinct_import_module_dependency_chain(
129 | rest_module_names,
130 | processed_module_names,
131 | )
132 | False -> {
133 | let unchecked_module_names =
134 | module_name
135 | |> module_name_to_file_name(SrcModuleKind)
136 | |> parse_module_for_imports
137 | |> list.append(module_names)
138 | |> list.filter(keeping: fn(module_name) {
139 | list.contains(processed_module_names, module_name) == False
140 | && file_exists(module_name_to_file_name(
141 | module_name,
142 | SrcModuleKind,
143 | ))
144 | })
145 | detect_distinct_import_module_dependency_chain(
146 | list.append(rest_module_names, unchecked_module_names),
147 | list.append(processed_module_names, [module_name]),
148 | )
149 | }
150 | }
151 | }
152 | }
153 |
154 | /// Parses a module file for its import statements.
155 | ///
156 | fn parse_module_for_imports(module_file_name: String) -> List(String) {
157 | module_file_name
158 | |> read_module_file()
159 | |> fn(result: Result(String, Nil)) -> List(String) {
160 | case result {
161 | Ok(text) ->
162 | text
163 | |> string.to_graphemes()
164 | |> parse_module_string([], ParseModeSearch, "")
165 | |> list.unique()
166 | Error(Nil) -> []
167 | }
168 | }
169 | }
170 |
171 | type ParseMode {
172 | ParseModeInComment
173 | ParseModeInString
174 | ParseModeSearch
175 | }
176 |
177 | /// Parses a module string for its import statements
178 | ///
179 | fn parse_module_string(
180 | chars: List(String),
181 | imports: List(String),
182 | context: ParseMode,
183 | collected: String,
184 | ) -> List(String) {
185 | case chars {
186 | [] -> imports
187 | [char, ..rest_chars] ->
188 | case context, collected, char {
189 | // Found `/`: Continue Initial with / in collected
190 | ParseModeSearch, "", "/" ->
191 | parse_module_string(rest_chars, imports, ParseModeSearch, "/")
192 | // Found `/` + `/`: Enter Comment
193 | ParseModeSearch, "/", "/" ->
194 | parse_module_string(rest_chars, imports, ParseModeInComment, "")
195 | // Found `"`: Enter String
196 | ParseModeSearch, _collected, "\"" ->
197 | parse_module_string(rest_chars, imports, ParseModeInString, "")
198 | // Collecting import keyword: Continue Initial
199 | ParseModeSearch, collected, char
200 | if collected == ""
201 | && char == "i"
202 | || collected == "i"
203 | && char == "m"
204 | || collected == "im"
205 | && char == "p"
206 | || collected == "imp"
207 | && char == "o"
208 | || collected == "impo"
209 | && char == "r"
210 | || collected == "impor"
211 | && char == "t"
212 | ->
213 | parse_module_string(
214 | rest_chars,
215 | imports,
216 | ParseModeSearch,
217 | collected <> char,
218 | )
219 | // Found `import` + whitespaceish: Enter Import
220 | ParseModeSearch, "import", char
221 | if char == " " || char == "\t" || char == "\n" || char == "\r\n"
222 | -> {
223 | let #(rest_chars, new_import) =
224 | parse_import_chars(rest_chars, string_tree.new())
225 | let new_import = string_tree.to_string(new_import)
226 | let updated_imports = [new_import, ..imports]
227 | parse_module_string(rest_chars, updated_imports, ParseModeSearch, "")
228 | }
229 | // Found `import\r` + "\n": Enter Import
230 | ParseModeSearch, "import\r", "\n" -> {
231 | let #(rest_chars, new_import) =
232 | parse_import_chars(rest_chars, string_tree.new())
233 | let imports = [string_tree.to_string(new_import), ..imports]
234 | parse_module_string(rest_chars, imports, ParseModeSearch, "")
235 | }
236 | // Found whitespaceish char: Continue Initial with empty collected
237 | ParseModeSearch, _collected, _char ->
238 | parse_module_string(rest_chars, imports, ParseModeSearch, "")
239 | // In Comment; found `\n`: Exit Comment
240 | ParseModeInComment, _collected, "\n" ->
241 | parse_module_string(rest_chars, imports, ParseModeSearch, "")
242 | // In Comment; found `\n`: Exit Comment
243 | ParseModeInComment, _collected, "\r" ->
244 | parse_module_string(rest_chars, imports, ParseModeSearch, "")
245 | ParseModeInComment, _collected, "\r\n" ->
246 | parse_module_string(rest_chars, imports, ParseModeSearch, "")
247 | // In Comment; found any other char: Continue Comment
248 | ParseModeInComment, _collected, _any ->
249 | parse_module_string(rest_chars, imports, ParseModeInComment, "")
250 | // In String; escape found char: Continue String
251 | ParseModeInString, "\\", _escaped ->
252 | parse_module_string(rest_chars, imports, ParseModeInString, "")
253 | // In String; found `"`: Exit String
254 | ParseModeInString, _collected, "\"" ->
255 | parse_module_string(rest_chars, imports, ParseModeSearch, "")
256 | // In String; found a single `\`: Continue String with collected set to `\`
257 | ParseModeInString, _collected, "\\" ->
258 | parse_module_string(rest_chars, imports, ParseModeInString, "\\")
259 | // In String; found any other char: Continue String with empty collected
260 | ParseModeInString, _collected, _char ->
261 | parse_module_string(rest_chars, imports, ParseModeInString, "")
262 | }
263 | }
264 | }
265 |
266 | /// Parses an import statement
267 | ///
268 | fn parse_import_chars(
269 | chars: List(String),
270 | import_module: string_tree.StringTree,
271 | ) {
272 | // TODO: Try pop grapheme
273 | case chars {
274 | // Return if end of line
275 | [] -> #([], import_module)
276 | // Return if . - aka found unqualified import
277 | [".", ..rest_chars] -> #(rest_chars, import_module)
278 | // Whitespaces stop inmports and return
279 | [" ", ..rest_chars] -> #(rest_chars, import_module)
280 | // Return if \r\n
281 | ["\r\n", ..rest_chars] -> #(rest_chars, import_module)
282 | // Return if \n
283 | ["\n", ..rest_chars] -> #(rest_chars, import_module)
284 | // Ignore whitespaces
285 | [char, ..rest_chars]
286 | if char == "\t" || char == "\r" || char == "\n" || char == "\r\n"
287 | -> parse_import_chars(rest_chars, import_module)
288 | // Append for any other character
289 | [char, ..rest_chars] ->
290 | parse_import_chars(rest_chars, string_tree.append(import_module, char))
291 | }
292 | }
293 |
294 | /// Derives test modules from its src import dependencies
295 | ///
296 | fn derive_test_modules_from_src_import_dependencies(
297 | src_modules: List(String),
298 | ) -> List(String) {
299 | let project_test_files = find_project_files(in: "test")
300 | let all_test_modules =
301 | project_test_files
302 | |> list.map(fn(module_name_dot_gleam) {
303 | let assert Ok(#(module_name, _dot_gleam)) =
304 | string.split_once(module_name_dot_gleam, ".gleam")
305 | module_name
306 | })
307 | let dirty_test_modules =
308 | all_test_modules
309 | |> list.filter(fn(test_module) {
310 | let test_module_imports = derive_src_imports_off_test_module(test_module)
311 | list.any(in: src_modules, satisfying: fn(src_module) {
312 | test_module_imports
313 | |> list.contains(src_module)
314 | })
315 | })
316 | dirty_test_modules
317 | }
318 |
319 | /// Derives src imports from test module
320 | ///
321 | fn derive_src_imports_off_test_module(test_module_name) {
322 | test_module_name
323 | |> module_name_to_file_name(TestModuleKind)
324 | |> parse_module_for_imports
325 | }
326 |
327 | /// Converts a module name to a module file name
328 | ///
329 | fn module_name_to_file_name(
330 | module_name: String,
331 | module_kind: ModuleKind,
332 | ) -> String {
333 | case module_kind {
334 | SrcModuleKind -> get_src_dir() <> "/" <> module_name <> ".gleam"
335 | TestModuleKind -> get_test_dir() <> "/" <> module_name <> ".gleam"
336 | }
337 | }
338 |
339 | /// Converts a module file name to a module name
340 | ///
341 | fn file_name_to_module_name(module_name: String, module_kind: ModuleKind) {
342 | let assert Ok(#(_base_path, module_name_dot_gleam)) = case module_kind {
343 | SrcModuleKind -> string.split_once(module_name, get_src_dir() <> "/")
344 | TestModuleKind -> string.split_once(module_name, get_test_dir() <> "/")
345 | }
346 | case string.ends_with(module_name, ".erl") {
347 | True -> {
348 | let assert Ok(#(module_name, _dot_gleam)) =
349 | string.split_once(module_name_dot_gleam, ".erl")
350 | module_name
351 | }
352 | False -> {
353 | let assert Ok(#(module_name, _dot_gleam)) =
354 | string.split_once(module_name_dot_gleam, ".gleam")
355 | module_name
356 | }
357 | }
358 | }
359 |
360 | /// Checks if a given absolute file path exists
361 | ///
362 | fn file_exists(absolute_file_name: String) -> Bool {
363 | do_file_exists(absolute_file_name)
364 | }
365 |
366 | /// Finds files in project sub directory
367 | ///
368 | fn find_project_files(in sub_directory: String) -> List(String) {
369 | do_find_project_files(sub_directory)
370 | }
371 |
372 | /// Gets the target at runtime
373 | ///
374 | fn target() -> Target {
375 | do_target()
376 | }
377 |
378 | /// Gets the start arguments sometimes called argv.
379 | ///
380 | fn start_args() -> List(String) {
381 | do_start_args()
382 | }
383 |
384 | /// Gets the current project directory.
385 | ///
386 | fn get_cwd() -> String {
387 | do_get_cwd()
388 | }
389 |
390 | /// Gets the project's `src` directory.
391 | ///
392 | fn get_src_dir() -> String {
393 | get_cwd() <> "/src"
394 | }
395 |
396 | /// Gets the project's `test` directory.
397 | ///
398 | fn get_test_dir() -> String {
399 | get_cwd() <> "/test"
400 | }
401 |
402 | /// Cuts off the base path from the project directory.
403 | ///
404 | fn to_relative_path(absolute_file_path path: String) -> String {
405 | let assert Ok(#(_pre_path, relative_file_name)) =
406 | string.split_once(path, get_cwd() <> "/")
407 | relative_file_name
408 | }
409 |
410 | @target(erlang)
411 | import shellout
412 | @target(erlang)
413 | import simplifile as file
414 |
415 | @target(erlang)
416 | fn do_target() -> Target {
417 | ErlangTarget
418 | }
419 |
420 | @target(erlang)
421 | import argv
422 |
423 | @target(erlang)
424 | fn do_start_args() -> List(String) {
425 | argv.load().arguments
426 | }
427 |
428 | @external(erlang, "glacier_ffi", "start_file_change_watcher")
429 | @external(javascript, "./glacier_ffi.mjs", "start_file_change_watcher")
430 | fn do_start_file_change_watcher(
431 | file_change_handler file_change_handler: fn(List(#(ModuleKind, String))) ->
432 | Nil,
433 | ) -> Nil
434 |
435 | @target(erlang)
436 | fn read_module_file(module_path: String) -> Result(String, Nil) {
437 | case file.read(module_path) {
438 | Ok(text) -> Ok(text)
439 | Error(file_reason) -> {
440 | #("Could not read file", module_path, "with reason", file_reason)
441 | |> string.inspect
442 | |> io.println_error
443 | Error(Nil)
444 | }
445 | }
446 | }
447 |
448 | @external(erlang, "glacier_ffi", "get_cwd_as_binary")
449 | @external(javascript, "./glacier_ffi.mjs", "cwd")
450 | fn do_get_cwd() -> String
451 |
452 | @external(erlang, "filelib", "is_regular")
453 | @external(javascript, "./glacier_ffi.mjs", "file_exists")
454 | fn do_file_exists(absolute_file_name absolute_file_name: String) -> Bool
455 |
456 | @target(erlang)
457 | fn do_find_project_files(in: String) -> List(String) {
458 | do_find_files_recursive(in: in, matching: "**/*.{gleam}")
459 | }
460 |
461 | @target(erlang)
462 | @external(erlang, "glacier_ffi", "find_files_recursive")
463 | fn do_find_files_recursive(
464 | in in: String,
465 | matching matching: String,
466 | ) -> List(String)
467 |
468 | @target(erlang)
469 | fn shell_exec_print(args: List(String)) -> Nil {
470 | case
471 | shellout.command(run: "gleam", with: args, in: ".", opt: [
472 | shellout.LetBeStderr,
473 | ])
474 | {
475 | Ok(msg) -> {
476 | io.print(msg)
477 | Nil
478 | }
479 | Error(_error_tuple) -> Nil
480 | }
481 | }
482 |
483 | @target(javascript)
484 | fn do_target() -> Target {
485 | JavaScriptTarget
486 | }
487 |
488 | @target(javascript)
489 | fn do_start_args() -> List(String) {
490 | start_args_ffi()
491 | // // This is a work-around for a bug introduced in Gleam 0.26.0:
492 | // |> list.filter(fn(arg) {
493 | // arg != "--" && string.ends_with(arg, "/gleam.main.mjs") == False
494 | // })
495 | }
496 |
497 | @target(javascript)
498 | @external(javascript, "./glacier_ffi.mjs", "start_args")
499 | fn start_args_ffi() -> List(String)
500 |
501 | @target(javascript)
502 | fn read_module_file(module_path: String) -> Result(String, Nil) {
503 | do_read_module_file(module_path)
504 | }
505 |
506 | @target(javascript)
507 | @external(javascript, "./glacier_ffi.mjs", "read_file")
508 | fn do_read_module_file(module_path module_path: String) -> Result(String, Nil)
509 |
510 | @target(javascript)
511 | fn do_find_project_files(dir: String) -> List(String) {
512 | do_find_files_recursive(dir, [".gleam"])
513 | |> list.map(fn(file_name) {
514 | let assert Ok(#(_test_prefix, file_name)) =
515 | string.split_once(file_name, "test/")
516 | file_name
517 | })
518 | }
519 |
520 | @target(javascript)
521 | @external(javascript, "./glacier_ffi.mjs", "find_files_recursive_by_exts")
522 | fn do_find_files_recursive(
523 | in in: String,
524 | file_ext file_ext: List(String),
525 | ) -> List(String)
526 |
527 | @target(javascript)
528 | @external(javascript, "./glacier_ffi.mjs", "shell_exec_print")
529 | fn shell_exec_print(args args: List(String)) -> Nil
530 |
--------------------------------------------------------------------------------
/src/glacier/should.gleam:
--------------------------------------------------------------------------------
1 | //// A proxy module into gleeunit/should
2 | ////
3 | //// If you are consistently using glacier (which wraps gleeunit) instead of
4 | //// gleeunit you may replace all `import gleeunit/should` with
5 | //// `import glacier/should`.
6 |
7 | import gleam/option.{type Option}
8 | import gleeunit/should as gleeunit_should
9 |
10 | pub fn equal(a: any, b: any) -> Nil {
11 | gleeunit_should.equal(a, b)
12 | }
13 |
14 | pub fn not_equal(a: any, b: any) -> Nil {
15 | gleeunit_should.not_equal(a, b)
16 | }
17 |
18 | pub fn be_ok(result: Result(a, b)) -> a {
19 | gleeunit_should.be_ok(result)
20 | }
21 |
22 | pub fn be_error(result: Result(a, b)) -> b {
23 | gleeunit_should.be_error(result)
24 | }
25 |
26 | pub fn be_some(a: Option(a)) -> a {
27 | gleeunit_should.be_some(a)
28 | }
29 |
30 | pub fn be_none(a: Option(a)) -> Nil {
31 | gleeunit_should.be_none(a)
32 | }
33 |
34 | pub fn be_true(actual: Bool) -> Nil {
35 | gleeunit_should.be_true(actual)
36 | }
37 |
38 | pub fn be_false(actual: Bool) -> Nil {
39 | gleeunit_should.be_false(actual)
40 | }
41 |
42 | pub fn fail() -> Nil {
43 | gleeunit_should.fail()
44 | }
45 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_module_a.gleam:
--------------------------------------------------------------------------------
1 | pub fn function_1() {
2 | 1
3 | }
4 |
5 | pub fn function_2() {
6 | 2
7 | }
8 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_module_aliasing_c.gleam:
--------------------------------------------------------------------------------
1 | import glacier_demo/glacier_demo_module_c as c
2 |
3 | pub fn proxied_function_5() {
4 | c.function_5()
5 | }
6 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_module_b.gleam:
--------------------------------------------------------------------------------
1 | //// module doc import in_module_docblock should be ignored
2 |
3 | import glacier_demo/glacier_demo_module_a
4 | import glacier_demo/glacier_demo_module_c
5 |
6 | /// function doc import in_fn_docblock should be ignored
7 | pub fn function_3() {
8 | // regular comment import in_comment should be ignored
9 | let _foo = "import string in string_1 should be ignored"
10 | let _bar = "in escaped \" string import string_2 should be ignored"
11 | let _quux = "in escaped \\ string import string_3 should be ignored"
12 | glacier_demo_module_a.function_1() + 2
13 | }
14 |
15 | pub fn function_4() {
16 | glacier_demo_module_c.function_5() - 1
17 | }
18 |
19 | @target(erlang)
20 | import gleam/string
21 |
22 | @target(erlang)
23 | pub fn a() {
24 | string.inspect("a")
25 | }
26 |
27 | @target(javascript)
28 | import gleam/string
29 |
30 | @target(javascript)
31 | pub fn a() {
32 | string.inspect("a")
33 | }
34 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_module_c.gleam:
--------------------------------------------------------------------------------
1 | import glacier_demo/glacier_demo_module_d
2 |
3 | pub fn function_5() {
4 | glacier_demo_module_d.main()
5 | 5
6 | }
7 |
8 | pub fn function_6() {
9 | 6
10 | }
11 |
12 | pub fn function_a() {
13 | "a"
14 | }
15 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_module_d.gleam:
--------------------------------------------------------------------------------
1 | // import in_comment
2 |
3 | pub fn main() {
4 | "import in_string a"
5 | }
6 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_module_e.gleam:
--------------------------------------------------------------------------------
1 | // import in_comment
2 |
3 | pub fn main() {
4 | Nil
5 | }
6 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_module_unqualifying.gleam:
--------------------------------------------------------------------------------
1 | import glacier_demo/glacier_demo_module_c.{function_5}
2 |
3 | pub fn proxied_function_5() {
4 | function_5()
5 | }
6 |
--------------------------------------------------------------------------------
/src/glacier_demo/glacier_demo_references_a.gleam:
--------------------------------------------------------------------------------
1 | import glacier_demo/glacier_demo_module_a
2 |
3 | pub fn function_1() {
4 | glacier_demo_module_a.function_1()
5 | }
6 |
7 | pub fn function_2() {
8 | 2
9 | }
10 |
--------------------------------------------------------------------------------
/src/glacier_demo/hello.gleam:
--------------------------------------------------------------------------------
1 | import glacier_demo/glacier_demo_module_a
2 |
3 | pub fn function_1() {
4 | glacier_demo_module_a.function_1()
5 | "hello"
6 | }
7 |
--------------------------------------------------------------------------------
/src/glacier_demo/other.txt:
--------------------------------------------------------------------------------
1 | //// This should not be handled as a gleam file because of the .txt extension
2 |
3 | import glacier_demo/glacier_demo_module_a
4 |
5 | pub fn function_1() {
6 | glacier_demo_module_a.function_1()
7 | }
8 |
9 | pub fn function_2() {
10 | 2
11 | }
12 |
--------------------------------------------------------------------------------
/src/glacier_ffi.erl:
--------------------------------------------------------------------------------
1 | -module(glacier_ffi).
2 |
3 | -export([start_file_change_watcher/1, get_cwd_as_binary/0, find_files_recursive/2]).
4 |
5 | get_cwd() ->
6 | {ok, Cwd} = file:get_cwd(),
7 | Cwd.
8 |
9 | get_cwd_as_binary() ->
10 | iolist_to_binary(get_cwd()).
11 |
12 | start_file_change_watcher(FileChangeHandlerFn) ->
13 | watch_directory(fs_src_watcher, src_module_kind, FileChangeHandlerFn),
14 | % watch_directory(fs_test_watcher, test_module_kind, FileChangeHandlerFn),
15 | timer:sleep(infinity),
16 | nil.
17 |
18 | process_file_update_and_loop(ModuleKind, WatchPath, FileChangeHandlerFn) ->
19 | receive
20 | {_Pid, {fs, file_event}, {FilePath, Changes}} ->
21 | % By stripping spaces we just do not support them at all in source
22 | % file names. This makes handling across targets and in regards do
23 | % module names and file name mapping a lot easier:
24 | FilePathNoSpaces = re:replace(FilePath, "\\s+", "", [global, {return, list}]),
25 | % Detect the parent dir type:
26 | CwdBinary = get_cwd_as_binary(),
27 | SrcDir = <>,
28 | TestDir = <>,
29 | IsSrcModuleKind = string:find(FilePath, SrcDir) =:= FilePathNoSpaces,
30 | IsTestModuleKind = string:find(FilePathNoSpaces, TestDir) =:= FilePathNoSpaces,
31 | % Make sure files are actually existing and end with .gleam:
32 | FileExists = filelib:is_regular(FilePathNoSpaces),
33 | IsGleamFile =
34 | gleam_stdlib:string_ends_with(list_to_bitstring(FilePathNoSpaces), <<".gleam"/utf8>>),
35 | MatchingEvent =
36 | lists:member(modified, Changes)
37 | orelse lists:member(created, Changes)
38 | orelse lists:member(renamed, Changes),
39 | case {FileExists, IsGleamFile, MatchingEvent, IsSrcModuleKind, IsTestModuleKind} of
40 | {true, true, true, true, _} ->
41 | % io:format("\n~p: ~p\n\n", [Changes, FilePathNoSpaces]),
42 | FileChangeHandlerFn([{src_module_kind, iolist_to_binary(FilePathNoSpaces)}]);
43 | {true, true, true, _, true} ->
44 | % io:format("\n~p: ~p \n\n", [Changes, FilePathNoSpaces]),
45 | FileChangeHandlerFn([{test_module_kind, iolist_to_binary(FilePathNoSpaces)}]);
46 | {_, _, _, _, _} ->
47 | nil
48 | end,
49 | process_file_update_and_loop(ModuleKind, WatchPath, FileChangeHandlerFn);
50 | _Any ->
51 | process_file_update_and_loop(ModuleKind, WatchPath, FileChangeHandlerFn)
52 | end.
53 |
54 | watch_directory(WatcherAtom, ModuleKind, FileChangeHandlerFn) ->
55 | Cwd = get_cwd(),
56 | WatchPath = Cwd,
57 | % SubPath = case ModuleKind of
58 | % src_module_kind -> "src";
59 | % test_module_kind -> "test"
60 | % end,
61 | % WatchPath = filename:join([Cwd, SubPath]),
62 | % io:format("~p ", [WatchPath]),
63 | fs:start_link(WatcherAtom, WatchPath),
64 | fs:subscribe(WatcherAtom),
65 | process_file_update_and_loop(ModuleKind, WatchPath, FileChangeHandlerFn),
66 | nil.
67 |
68 | % Derived from gleeunit_ffi.erl
69 | find_files_recursive(In, Pattern) ->
70 | Results = filelib:wildcard(binary_to_list(Pattern), binary_to_list(In)),
71 | lists:map(fun list_to_binary/1, Results).
72 |
--------------------------------------------------------------------------------
/src/glacier_ffi.mjs:
--------------------------------------------------------------------------------
1 | import { SrcModuleKind, TestModuleKind } from "./glacier.mjs";
2 | import * as Gleam from "./gleam.mjs";
3 | import child_process from 'node:child_process';
4 | import fs from "node:fs";
5 | import fs_promises from "node:fs/promises";
6 | import path from "node:path";
7 | import process from 'node:process';
8 |
9 | const file_change_watcher_debounce_interval_in_ms = 100;
10 | const Nil = undefined; // Translates to `Nil` in Gleam
11 |
12 | ['SIGINT', 'SIGTERM', 'SIGQUIT']
13 | .forEach(signal => process.on(signal, function () {
14 | console.log("\n🏔 Gracefully shutting down Glacier from SIGINT (Ctrl-C)!");
15 | process.exit(0);
16 | }));
17 |
18 | process.on('warning', function (e) {
19 | console.warn(e.stack);
20 | });
21 |
22 | export const start_args = function () {
23 | return Gleam.List.fromArray(process.argv.slice(2));
24 | };
25 |
26 | export const cwd = function () {
27 | return process.cwd();
28 | };
29 |
30 | export const start_file_change_watcher = function (file_change_handler_fn) {
31 | let file_change_handler_timeout_id = null;
32 | let file_change_handler_collection = [];
33 | const watch_directory = async function (directory, observed_events, file_change_handler_fn, module_kind) {
34 | let watcher = undefined;
35 | if (globalThis.Deno) {
36 | watcher = Deno.watchFs([directory], { recursive: true });
37 | } else {
38 | watcher = fs_promises.watch(directory, { persistent: true, recursive: true });
39 | }
40 | for await (const event of watcher) {
41 | const event_kind = function () {
42 | if (globalThis.Deno) {
43 | return event.kind;;
44 | } {
45 | return event.eventType;
46 | }
47 | }();
48 | const touched_file = function () {
49 | if (globalThis.Deno) {
50 | return event.paths[0];
51 | } {
52 | return directory + "/" + event.filename;
53 | }
54 | }();
55 | if (observed_events.includes(event_kind) && touched_file.endsWith(".gleam")) {
56 | if (file_change_handler_timeout_id !== null) {
57 | clearTimeout(file_change_handler_timeout_id);
58 | }
59 | file_change_handler_collection.push([module_kind, touched_file]);
60 | file_change_handler_timeout_id = setTimeout(function () {
61 | // NodeJS fs.watch is prone to report the same change twice, thus we need to distinct the changes:
62 | let distinct_file_change_handler_collection = [...new Set(file_change_handler_collection)];
63 | // As we collect file on a delay set by file_change_watcher_debounce_interval_in_ms,
64 | // they could be gone once we want to handle them:
65 | distinct_file_change_handler_collection = distinct_file_change_handler_collection.filter(function (file_info) {
66 | let absolute_file_name = file_info[1];
67 | absolute_file_name = absolute_file_name.replace(/\s/g, '');
68 | return file_exists(absolute_file_name);
69 | });
70 | if (distinct_file_change_handler_collection.length > 0) {
71 | file_change_handler_fn(Gleam.List.fromArray(distinct_file_change_handler_collection));
72 | file_change_handler_timeout_id = null;
73 | file_change_handler_collection = [];
74 | }
75 | }, file_change_watcher_debounce_interval_in_ms);
76 | }
77 | }
78 | };
79 | watch_directory(cwd() + "/src", ["change", "rename", "modify"], file_change_handler_fn, new SrcModuleKind());
80 | watch_directory(cwd() + "/test", ["change", "rename", "modify"], file_change_handler_fn, new TestModuleKind());
81 |
82 | return Nil;
83 | };
84 |
85 | export const read_file = function (absolute_file_name) {
86 | try {
87 | const data = fs.readFileSync(absolute_file_name, 'utf8');
88 | return new Gleam.Ok(data);
89 | } catch (err) {
90 | // console.error({"Could not read file" : err});
91 | return new Gleam.Error(Nil);
92 | }
93 | };
94 |
95 | export const file_exists = function (absolute_file_name) {
96 | if (fs.existsSync(absolute_file_name)) {
97 | return true;
98 | }
99 | return false;
100 | };
101 |
102 | export const find_files_recursive_by_exts = function (directory, file_exts_list) {
103 | file_exts_list = file_exts_list.toArray();
104 | let files = [];
105 | /* mut files */ const detect_files_recursive = function (directory) {
106 | const files_in_directory = fs.readdirSync(directory);
107 | for (const file of files_in_directory) {
108 | let absolute_path = path.join(directory, file);
109 | absolute_path = absolute_path.replace(/\s/g, '');
110 | if (fs.statSync(absolute_path).isDirectory()) {
111 | detect_files_recursive(absolute_path);
112 | } else if (absolute_path.endsWith(file_exts_list) && file_exists(absolute_path)) {
113 | files.push(absolute_path);
114 | }
115 | }
116 | };
117 | detect_files_recursive(directory);
118 | return Gleam.List.fromArray(files);
119 | };
120 |
121 | export const shell_exec_print = async function (gleam_list_of_graphemes) {
122 | if (globalThis.Deno) {
123 | Deno.run({
124 | cmd: ["gleam", ...gleam_list_of_graphemes.toArray()]
125 | });
126 | } else {
127 | const cmd = "gleam " + gleam_list_of_graphemes.toArray().join(" ");
128 | let { stdout } = await node_shell_exec(cmd);
129 | for (let line of stdout.split('\n')) {
130 | console.log(`${line}`);
131 | }
132 | }
133 | }
134 |
135 | const node_shell_exec = async function (cmd) {
136 | return new Promise(function (resolve, reject) {
137 | child_process.exec(cmd, (err, stdout, stderr) => {
138 | if (err) {
139 | reject(err);
140 | } else {
141 | resolve({ stdout, stderr });
142 | }
143 | });
144 | });
145 | }
146 |
--------------------------------------------------------------------------------
/test/glacier_demo/glacier_demo_module_a_test.gleam:
--------------------------------------------------------------------------------
1 | import glacier/should
2 | import glacier_demo/glacier_demo_module_a
3 |
4 | pub fn function_1_test() {
5 | glacier_demo_module_a.function_1()
6 | |> should.equal(1)
7 | }
8 |
9 | pub fn function_2_test() {
10 | glacier_demo_module_a.function_2()
11 | |> should.equal(2)
12 | }
13 |
--------------------------------------------------------------------------------
/test/glacier_demo/glacier_demo_module_b_test.gleam:
--------------------------------------------------------------------------------
1 | import glacier/should
2 | import glacier_demo/glacier_demo_module_b
3 |
4 | pub fn function_3_test() {
5 | glacier_demo_module_b.function_3()
6 | |> should.equal(3)
7 | }
8 |
9 | pub fn function_4_test() {
10 | glacier_demo_module_b.function_4()
11 | |> should.equal(4)
12 | }
13 |
--------------------------------------------------------------------------------
/test/glacier_demo/glacier_demo_module_c_test.gleam:
--------------------------------------------------------------------------------
1 | import glacier/should
2 | import glacier_demo/glacier_demo_module_c
3 |
4 | pub fn function_5_test() {
5 | glacier_demo_module_c.function_5()
6 | |> should.equal(5)
7 | }
8 |
9 | pub fn function_6_test() {
10 | glacier_demo_module_c.function_6()
11 | |> should.equal(6)
12 | }
13 |
14 | pub fn function_6b_test() {
15 | glacier_demo_module_c.function_6()
16 | |> should.equal(6)
17 | }
18 |
--------------------------------------------------------------------------------
/test/glacier_demo/glacier_demo_module_gleeunit_should.gleam:
--------------------------------------------------------------------------------
1 | import glacier_demo/glacier_demo_module_c
2 | import gleeunit/should
3 |
4 | pub fn function_5_test() {
5 | glacier_demo_module_c.function_5()
6 | |> should.equal(5)
7 | }
8 |
9 | pub fn function_6_test() {
10 | glacier_demo_module_c.function_6()
11 | |> should.equal(6)
12 | }
13 |
14 | pub fn function_6b_test() {
15 | glacier_demo_module_c.function_6()
16 | |> should.equal(6)
17 | }
18 |
--------------------------------------------------------------------------------
/test/glacier_test.gleam:
--------------------------------------------------------------------------------
1 | import glacier
2 |
3 | pub fn main() {
4 | glacier.main()
5 | }
6 |
--------------------------------------------------------------------------------