1 | <div align="center">
|
2 |
|
3 | # [![Jewire](logo.svg)](https://github.com/nktnet1/jewire)
|
4 |
|
5 | [![pipeline](https://github.com/nktnet1/jewire/actions/workflows/pipeline.yml/badge.svg)](https://github.com/nktnet1/jewire/actions/workflows/pipeline.yml)
|
6 |
|
7 | [![codecov](https://codecov.io/gh/nktnet1/jewire/branch/main/graph/badge.svg?token=RAC7SKJTGU)](https://codecov.io/gh/nktnet1/jewire)
|
8 |
|
9 | [![Maintainability](https://api.codeclimate.com/v1/badges/2cc2478a1b5a2f293149/maintainability)](https://codeclimate.com/github/nktnet1/jewire/maintainability)
|
10 |
|
11 | [![Snyk Security](https://snyk.io/test/github/nktnet1/jewire/badge.svg)](https://snyk.io/test/github/nktnet1/jewire)
|
12 |
|
13 | [![GitHub top language](https://img.shields.io/github/languages/top/nktnet1/jewire)](https://github.com/search?q=repo%3Anktnet1%2Fjewire++language%3ATypeScript&type=code)
|
14 |
|
15 | [![NPM Version](https://img.shields.io/npm/v/jewire?logo=npm)](https://www.npmjs.com/package/jewire?activeTab=versions)
|
16 |
|
17 | [![install size](https://packagephobia.com/badge?p=jewire)](https://packagephobia.com/result?p=jewire)
|
18 |
|
19 | [![Depfu Dependencies](https://badges.depfu.com/badges/6c4074c4d23ad57ee2bfd9ff90456090/overview.svg)](https://depfu.com/github/nktnet1/jewire?project_id=39032)
|
20 |
|
21 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnktnet1%2Fjewire.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnktnet1%2Fjewire?ref=badge_shield)
|
22 |
|
23 | [![NPM License](https://img.shields.io/npm/l/jewire)](https://opensource.org/license/mit/)
|
24 |
|
25 | [![GitHub issues](https://img.shields.io/github/issues/nktnet1/jewire.svg?style=social)](https://github.com/nktnet1/jewire/issues)
|
26 |
|
27 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=nktnet1_jewire&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=nktnet1_jewire)
|
28 |
|
29 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/65161ae4d1c646ed83c9ef47b0a11473)](https://app.codacy.com/gh/nktnet1/jewire/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
30 |
|
31 | [![DeepSource](https://app.deepsource.com/gh/nktnet1/jewire.svg/?label=active+issues&show_trend=true&token=OTP6tE2be4X1kvxZRsxRh25e)](https://app.deepsource.com/gh/nktnet1/jewire/)
|
32 |
|
33 | [![codebeat badge](https://codebeat.co/badges/8bdb4562-0492-4c1c-8b02-e69c94373d60)](https://codebeat.co/projects/github-com-nktnet1-jewire-main)
|
34 |
|
35 | [![GitHub stars](https://img.shields.io/github/stars/nktnet1/jewire.svg?style=social)](https://github.com/nktnet1/jewire/stargazers)
|
36 |
|
37 | [![Downloads Total](https://badgen.net/npm/dt/jewire)](https://moiva.io/?npm=jewire)
|
38 |
|
39 | [![Downloads Yearly](https://badgen.net/npm/dy/jewire)](https://moiva.io/?npm=jewire)
|
40 |
|
41 | [![Downloads Monthly](https://badgen.net/npm/dm/jewire)](https://moiva.io/?npm=jewire)
|
42 |
|
43 | [![Downloads Weekly](https://badgen.net/npm/dw/jewire)](https://moiva.io/?npm=jewire)
|
44 |
|
45 | [![Downloads Daily](https://badgen.net/npm/dd/jewire)](https://moiva.io/?npm=jewire)
|
46 |
|
47 | ---
|
48 |
|
49 | Access private functions, variables and classes from CommonJS modules
|
50 |
|
51 | Enable named exports with relative paths identical to CommonJS [require](https://nodejs.org/api/modules.html#requireid)
|
52 |
|
53 | Clone objects at runtime to remove false negatives in expect[.toStrictEqual](https://jestjs.io/docs/expect#tostrictequalvalue)
|
54 |
|
55 | [![Try with Replit](https://replit.com/badge?caption=Try%20with%20Replit)](https://replit.com/@nktnet1/jewire-example#index.js)
|
56 |
|
57 | </div>
|
58 |
|
59 | ---
|
60 |
|
61 | - [1. Installation](#1-installation)
|
62 | - [2. Usage](#2-usage)
|
63 | - [2.1. relativePath](#21-relativepath)
|
64 | - [2.2. options](#22-options)
|
65 | - [2.3. return](#23-return)
|
66 | - [3. License](#3-license)
|
67 | - [4. Limitations](#4-limitations)
|
68 | - [5. Caveats](#5-caveats)
|
69 | - [5.1. Purpose](#51-purpose)
|
70 | - [5.2. Rationale](#52-rationale)
|
71 | - [5.3. Rewire and Jest](#53-rewire-and-jest)
|
72 |
|
73 | ## 1. Installation
|
74 |
|
75 | ```
|
76 | npm install jewire
|
77 | ```
|
78 |
|
79 | ## 2. Usage
|
80 |
|
81 | Try with [Replit](https://replit.com/@nktnet1/jewire-example#index.js).
|
82 |
|
83 | ```
|
84 | jewire(relativePath, options);
|
85 | ```
|
86 |
|
87 | <details closed>
|
88 | <summary>Examples (click to view)</summary>
|
89 |
|
90 | <br/>
|
91 |
|
92 | Importing from the same directory
|
93 |
|
94 | ```javascript
|
95 | const { privateVariable, privateFunction } = jewire('private-module');
|
96 | ```
|
97 |
|
98 | Importing `.cjs` file from a different directory
|
99 |
|
100 | ```javascript
|
101 | const { privateFunction } = jewire('../src/private-module.cjs');
|
102 | ```
|
103 |
|
104 | Using a different basePath
|
105 |
|
106 | ```javascript
|
107 | const { privateFunction } = jewire(
|
108 | 'private-module',
|
109 | { basePath: process.cwd() }
|
110 | );
|
111 | ```
|
112 |
|
113 | Using a different clone function from the default `clone.objectClone`:
|
114 |
|
115 | ```javascript
|
116 | const { privateFunction } = jewire(
|
117 | 'private-module',
|
118 | { objectClone: (obj) => JSON.parse(JSON.stringify(obj)) }
|
119 | );
|
120 | ```
|
121 |
|
122 | </details>
|
123 |
|
124 | <br/>
|
125 |
|
126 | ### 2.1. Parameter: relativePath
|
127 |
|
128 | Path to the module relative to the current file, similar to CommonJS [require](https://nodejs.org/api/modules.html#requireid). For example,
|
129 | - `'../animals/cats.js'`
|
130 | - `'./common.cjs'`
|
131 | - `'minimal'`
|
132 | - `jewire` will look for `'./minimal.js'` before `'./minimal.cjs'`
|
133 |
|
134 | Note that `option.basePath` can be provided to alter this behaviour.
|
135 |
|
136 | ### 2.2. Parameter: options
|
137 |
|
138 | <table>
|
139 | <tr>
|
140 | <th>Option</th>
|
141 | <th>Description</th>
|
142 | <th>Example</th>
|
143 | <th>Default</th>
|
144 | </tr>
|
145 |
|
146 | <tr>
|
147 | <td>basePath</td>
|
148 | <td>Absolute path to the module's directory</td>
|
149 | <td>
|
150 | <pre>
|
151 | process.cwd()
|
152 | </pre>
|
153 | </td>
|
154 | <td><code>__dirname</code></td>
|
155 | </tr>
|
156 |
|
157 | <td>
|
158 | objectClone
|
159 | </td>
|
160 | <td>
|
161 | Cloning function to apply to any objects or arrays that are global symbols, function return values or class method return values.
|
162 | </td>
|
163 | <td>
|
164 | <pre>
|
165 | o => JSON.parse(
|
166 | JSON.stringify(
|
167 | o
|
168 | )
|
169 | )
|
170 | </pre>
|
171 | </td>
|
172 | <td>
|
173 | <code>objectClone</code>
|
174 | <br />
|
175 | (see <a href='src/clone.ts'>clone.ts</a>)
|
176 | </td>
|
177 |
|
178 | </tr>
|
179 | </table>
|
180 |
|
181 | ### 2.3. Return Object
|
182 |
|
183 | All named exports, including globally defined variables, functions and classes are returned as keys within an object.
|
184 |
|
185 | Additionally, the returned object contains the key `__jewireContext__` with the values being another object with the following properties:
|
186 |
|
187 | 1. `rewire`: the return value of `rewire(modulePath)`. Please refer to the documentation for [rewire](https://github.com/jhnns/rewire) for further details.
|
188 | 2. `hiddenExportInfo`: An object containing information about all hidden exports, of the form:
|
189 |
|
190 | ```javascript
|
191 | {
|
192 | symbols: {
|
193 | variables: string[],
|
194 | functions: string[],
|
195 | classes: string[],
|
196 | }
|
197 | ast: ASTProgram, // Abstract Syntax Tree from Meriyah parser
|
198 | code: string, // return value of fs.readFileSync(filePath)
|
199 | }
|
200 | ```
|
201 | 3. `jewireGetter`: the internal function used by jewire to retrieve objects. It has the same prototype as [`rewireModule.__get__`](https://github.com/jhnns/rewire?tab=readme-ov-file#rewiredmodule__get__name-string-), although objects are deep cloned using either jewire's default clone function or, if provided, `options.objectClone`.
|
202 |
|
203 | ### 2.4. Errors
|
204 |
|
205 | If an unknown file path is provided, the given file is empty or the module contains invalid code such as syntax errors, a default [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error) object is thrown.
|
206 |
|
207 | ## 3. License
|
208 |
|
209 | <details closed>
|
210 | <summary>
|
211 | Massachusetts Institute of Technology
|
212 | (<a href="https://opensource.org/license/mit" target="_blank">MIT</a>)
|
213 | </summary>
|
214 |
|
215 | <br/>
|
216 |
|
217 | ```
|
218 | Copyright (c) 2023 Khiet Tam Nguyen
|
219 |
|
220 | Permission is hereby granted, free of charge, to any person obtaining a
|
221 | copy of this software and associated documentation files (the “Software”),
|
222 | to deal in the Software without restriction, including without limitation
|
223 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
224 | and/or sell copies of the Software, and to permit persons to whom the
|
225 | Software is furnished to do so, subject to the following conditions:
|
226 |
|
227 | The above copyright notice and this permission notice shall be included in
|
228 | all copies or substantial portions of the Software.
|
229 |
|
230 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
231 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
232 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
233 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
234 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
235 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
236 | DEALINGS IN THE SOFTWARE.
|
237 | ```
|
238 |
|
239 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnktnet1%2Fjewire.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnktnet1%2Fjewire?ref=badge_large)
|
240 |
|
241 | </details>
|
242 |
|
243 | ## 4. Limitations
|
244 |
|
245 | As this library uses [rewire](https://github.com/jhnns/rewire) under the hood,
|
246 | it has the same limitations in that it can only import CommonJS modules. Please
|
247 | see the [limitations](https://github.com/jhnns/rewire#limitations) as described
|
248 | in the rewire module.
|
249 |
|
250 | ## 5. Caveats
|
251 |
|
252 | ### 5.1. Purpose
|
253 |
|
254 | **jewire** is a niche library designed to automark private functions in the
|
255 | submitted code of students using the Jest testing framework during the first
|
256 | 2 weeks of their studies in
|
257 | [COMP1531 Software Engineering Fundamentals](https://webcms3.cse.unsw.edu.au/COMP1531/23T2/outline).
|
258 |
|
259 | This is because `npm` and module imports/exports are not introduced until week
|
260 | 3, when students are considered to be more familiar with JavaScript as a
|
261 | programming language.
|
262 |
|
263 | ### 5.2. Rationale
|
264 |
|
265 | **jewire** aims to simplify the process of using
|
266 | [rewire](https://github.com/jhnns/rewire)
|
267 | by removing the need to provide a file extension and absolute path, abstracting
|
268 | getter and setter methods and enabling relative module imports similar to CommonJS
|
269 | [require](https://nodejs.org/api/modules.html).
|
270 |
|
271 | This process requires reading the module twice - once to parse into an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) using the [Meriyah](https://github.com/meriyah/meriyah) parser, and another from rewire as rewire's interface does not enable reusing the file content.
|
272 |
|
273 | ### 5.3. Rewire and Jest
|
274 |
|
275 | **Jewire** reclones objects at runtime to allow the return values of rewired
|
276 | functions and class methods to be compared using
|
277 | [Jest](https://jestjs.io)'s
|
278 | expect[.toStrictEqual](https://jestjs.io/docs/expect#tostrictequalvalue) matcher,
|
279 | which in the rewire module would yield *"Received: serializes to the same
|
280 | string"*.
|
281 |
|
282 | The cause is Jest's utilisation of `node:vm` under the hood, which creates its own temporary context that overrides global classes such as `Array`, `Error` and `Date` to extend functionalities. These global classes differ from those that are returned from [rewire](https://github.com/jhnns/rewire)'s private functions, as depicted in the following GitHub issues made by [@geogezlei](https://github.com/georgezlei):
|
283 | - https://github.com/jhnns/rewire/issues/164
|
284 | - https://github.com/jestjs/jest/issues/8446
|
285 |
|
286 | For further details and explanation about this problem, please visit Manuel Spigolon's [article](https://backend.cafe/should-you-use-jest-as-a-testing-library) about the use of the `instanceof` operator in Jest.
|
287 |
|