├── .gitignore
├── LICENSE
├── README.md
├── index.js
├── package.json
├── pnpm-lock.yaml
├── screenshots
├── bare.png
├── changed.png
├── cli.png
├── deduplication.png
├── info.png
├── life-cycle-events.png
├── markdown.png
├── message.gif
├── return-value.png
└── trace.png
├── src
├── development.ts
└── production.ts
├── test
└── basic.test.tsx
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Shu Ding
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # `useTilg`
2 |
3 | **Tiny Logger** is a magical React Hook to help you debug your components.
4 |
5 | You can quickly try out the [**demo**](https://codesandbox.io/s/usetilg-3kdtz8?file=/src/App.js:274-359).
6 |
7 |
8 |
9 | ## Table of Contents
10 |
11 | - [Installation](#installation)
12 | - [Features](#features)
13 | - [Lifecycle Events (What)](#1-lifecycle-events-what)
14 | - [Component Name and Props (Who)](#2-component-name-and-props-who)
15 | - [Debug Message (Why)](#3-debug-message-why)
16 | - [What Has Changed? (Why)](#4-what-has-changed-why)
17 | - [Quick Logs (Why)](#5-quick-logs-why)
18 | - [Advanced Features](#advanced-features)
19 | - [Markdown](#markdown)
20 | - [Return Original Value](#return-original-value)
21 | - [Auto Deduplication](#auto-deduplication)
22 | - [CLI Support](#cli-support)
23 | - [FAQ & Caveats](#faq--caveats)
24 |
25 |
26 |
27 | ## Installation
28 |
29 | The package is released as `tilg`, use:
30 |
31 | ```sh
32 | npm i tilg
33 | ```
34 |
35 | to install it with npm. Or you can choose another package manager.
36 |
37 |
38 |
39 | ## Features
40 |
41 | ### 1. Lifecycle Events (What)
42 |
43 | Simply insert the `useTilg()` hook into the component, and it will log the **render**, **mount**, **unmount** events in the console:
44 |
45 | ```jsx
46 | import useTilg from 'tilg'
47 |
48 | function MyButton() {
49 | useTilg()
50 | return Click me
51 | }
52 | ```
53 |
54 |
55 |
56 |
57 | Logs of render and mount events.
58 |
59 |
60 |
61 | ### 2. Component Name and Props (Who)
62 |
63 | You might noticed that it also displays the **name** and **props** of the component, which is very helpful for debugging.
64 |
65 | ```jsx
66 | import useTilg from 'tilg'
67 |
68 | function MyButton({ text }) {
69 | useTilg()
70 | return {text}
71 | }
72 |
73 | function Title({ children }) {
74 | useTilg()
75 | return {children}
76 | }
77 |
78 | export default function Page() {
79 | return (
80 | <>
81 | Welcome!
82 |
83 |
84 | >
85 | )
86 | }
87 | ```
88 |
89 | When there’re multiple elements of the same component being rendered, it adds a counter ` (2)` for distinguishing so you know **who** is logging the information:
90 |
91 |
92 |
93 |
94 | Information of the logged components.
95 |
96 |
97 |
98 | ### 3. Debug Message (Why)
99 |
100 | Another critical thing is to know why does a component re-renders. `useTilg` gives you a simple but powerful API for this:
101 |
102 | ```jsx
103 | import { useState } from 'react'
104 | import useTilg from 'tilg'
105 |
106 | function Counter() {
107 | const [count, setCount] = useState(0)
108 |
109 | useTilg()`count = ${count}`
110 |
111 | return setCount(count + 1)}>{count}
112 | }
113 | ```
114 |
115 | When appending a [template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) to the `useTilg()` call, it will also be displayed as the debug message:
116 |
117 | ```jsx
118 | useTilg()`count = ${count}`
119 | ```
120 |
121 |
122 |
123 |
124 | Logs of “count = ?”.
125 |
126 |
127 | You can know where the message is from, too:
128 |
129 |
130 |
131 |
132 | Trace of the message and a link to the code location.
133 |
134 |
135 | ### 4. What Has Changed? (Why)
136 |
137 | Something troubles me a lot when debugging a component is, it’s sometimes hard to know which state has changed and triggered a re-render. `useTilg` tracks all the arguments in the debug message and tells you **which one has changed since the previous render**:
138 |
139 | ```jsx
140 | import { useState } from 'react'
141 | import useTilg from 'tilg'
142 |
143 | function MyApp() {
144 | const [input, setInput] = useState('')
145 | const [count, setCount] = useState(0)
146 |
147 | useTilg()`input = ${input}, count = ${count}`
148 |
149 | return (
150 | <>
151 | setInput(e.target.value)} value={input} />
152 | setCount(count + 1)}>{count}
153 | >
154 | )
155 | }
156 | ```
157 |
158 |
159 |
160 |
161 | A hint for the updated part.
162 |
163 |
164 |
165 | ### 5. Quick Logs (Why)
166 |
167 | If you don't need a debug message but only want to quickly log some values, just pass them to the hook directly:
168 |
169 | ```jsx
170 | import { useState } from 'react'
171 | import useTilg from 'tilg'
172 |
173 | function MyApp() {
174 | const [input, setInput] = useState('')
175 | const [count, setCount] = useState(0)
176 |
177 | useTilg(input, count)
178 |
179 | return (
180 | <>
181 | setInput(e.target.value)} value={input} />
182 | setCount(count + 1)}>{count}
183 | >
184 | )
185 | }
186 | ```
187 |
188 |
189 |
190 |
191 | Debug values quickly.
192 |
193 |
194 |
195 |
196 |
197 | ## Advanced Features
198 |
199 | ### Markdown
200 |
201 | You can use Markdown's code (`` ` ``), italic (`_` or `*`), and bold (`__` or `**`) syntax in your debug message to make it look nicer:
202 |
203 | ```jsx
204 | function MyApp() {
205 | const [count, setCount] = useState(0)
206 |
207 | useTilg()`**Debug**: \`count\` = _${count}_.`
208 |
209 | return setCount(count + 1)}>{count}
210 | }
211 | ```
212 |
213 |
214 |
215 |
216 | Markdown syntax in log messages.
217 |
218 |
219 |
220 | ### Return Original Value
221 |
222 | The `useTilg()` hook also returns its **first argument**, or the **first value** in the template if specified, so you can quickly debug something in-place by wrapping it with `useTilg()`:
223 |
224 | ```diff
225 | function MyApp() {
226 | const [count, setCount] = useState(0)
227 |
228 | return setCount(count + 1)}>{
229 | + useTilg(count)
230 | }
231 | }
232 | ```
233 |
234 |
235 |
236 |
237 |
238 | Log and return the original value.
239 |
240 |
241 |
242 | ### Auto Deduplication
243 |
244 | Even if you have multiple `useTilg()` hooks in the same component, the lifecycle events will only be logged once per component:
245 |
246 | ```jsx
247 | function MyApp() {
248 | const [input, setInput] = useState('')
249 | const [count, setCount] = useState(0)
250 |
251 | useTilg()
252 | useTilg()`input = ${input}`
253 | useTilg()`count = ${count}`
254 |
255 | return (
256 | <>
257 | setInput(e.target.value)} value={input} />
258 | setCount(count + 1)}>{count}
259 | >
260 | )
261 | }
262 | ```
263 |
264 |
265 |
266 |
267 | Render, mount, and unmount events will not be duplicated even if you have multiple useTilg() hooks.
268 |
269 |
270 | ### CLI Support
271 |
272 | If you are running your component during SSR, or running server-side tests, `useTilg()` properly outputs the result in Node.js CLI too:
273 |
274 | ```jsx
275 | function App() {
276 | const [count, setCount] = useState(42)
277 |
278 | useTilg()`The answer is ${{ answer: count }}`
279 |
280 | return setCount(count + 1)}>{count}
281 | }
282 | ```
283 |
284 |
285 |
286 |
287 | Node.js CLI output.
288 |
289 |
290 |
291 |
292 | ## FAQ & Caveats
293 |
294 | - **Is it safe to ship code with `useTilg` to production?**
295 | Although `useTilg()` does nothing in a production build (`process.env.NODE_ENV === 'production'`) but only an empty function, I encourge you to remove the hook from the source code after finishing the development of your app.
296 |
297 | - **How do you implement this hook? What can I learn from the code?**
298 | It is very hacky. Don't do the same thing especially try it in production, or [you will be fired](https://github.com/facebook/react/blob/0568c0f8cde4ac6657dff9a5a8a7112acc35a748/packages/react/index.js#L35).
299 |
300 | - **Why not design the API as `` useTilg`message` ``?**
301 | Then it will not be identified as a hook, React Refresh and HMR will not work correctly.
302 |
303 |
304 |
305 | ## License
306 |
307 | The MIT License (MIT).
308 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV === 'production') {
2 | module.exports = require('./dist/production.js')
3 | } else {
4 | module.exports = require('./dist/development.js')
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tilg",
3 | "version": "0.1.1",
4 | "description": "A tiny logger hook for debugging React components.",
5 | "keywords": [
6 | "react",
7 | "react hook",
8 | "logger",
9 | "debug"
10 | ],
11 | "main": "index.js",
12 | "types": "dist/development.d.ts",
13 | "files": [
14 | "LICENSE",
15 | "README.md",
16 | "dist/**"
17 | ],
18 | "scripts": {
19 | "build": "pnpm build:dev && pnpm build:prod",
20 | "build:dev": "tsup src/development.ts --dts --env.NODE_ENV development",
21 | "build:prod": "tsup src/production.ts --env.NODE_ENV production",
22 | "coverage": "vitest run --coverage",
23 | "test": "vitest"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/shuding/tilg.git"
28 | },
29 | "author": "Shu Ding",
30 | "license": "MIT",
31 | "bugs": {
32 | "url": "https://github.com/shuding/tilg/issues"
33 | },
34 | "homepage": "https://github.com/shuding/tilg#readme",
35 | "devDependencies": {
36 | "@types/node": "^17.0.23",
37 | "@types/react": "^18.0.0",
38 | "@types/react-test-renderer": "^17.0.1",
39 | "react": "^18.0.0",
40 | "react-test-renderer": "^18.0.0",
41 | "tsup": "^5.12.4",
42 | "typescript": "^4.6.3",
43 | "vitest": "^0.9.2"
44 | },
45 | "peerDependencies": {
46 | "react": "^18.0.0 || ^17.0.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: 5.3
2 |
3 | specifiers:
4 | '@types/node': ^17.0.23
5 | '@types/react': ^18.0.0
6 | '@types/react-test-renderer': ^17.0.1
7 | react: ^18.0.0
8 | react-test-renderer: ^18.0.0
9 | tsup: ^5.12.4
10 | typescript: ^4.6.3
11 | vitest: ^0.9.2
12 |
13 | devDependencies:
14 | '@types/node': 17.0.23
15 | '@types/react': 18.0.0
16 | '@types/react-test-renderer': 17.0.1
17 | react: 18.0.0
18 | react-test-renderer: 18.0.0_react@18.0.0
19 | tsup: 5.12.4_typescript@4.6.3
20 | typescript: 4.6.3
21 | vitest: 0.9.2
22 |
23 | packages:
24 |
25 | /@nodelib/fs.scandir/2.1.5:
26 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
27 | engines: {node: '>= 8'}
28 | dependencies:
29 | '@nodelib/fs.stat': 2.0.5
30 | run-parallel: 1.2.0
31 | dev: true
32 |
33 | /@nodelib/fs.stat/2.0.5:
34 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
35 | engines: {node: '>= 8'}
36 | dev: true
37 |
38 | /@nodelib/fs.walk/1.2.8:
39 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
40 | engines: {node: '>= 8'}
41 | dependencies:
42 | '@nodelib/fs.scandir': 2.1.5
43 | fastq: 1.13.0
44 | dev: true
45 |
46 | /@types/chai-subset/1.3.3:
47 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
48 | dependencies:
49 | '@types/chai': 4.3.0
50 | dev: true
51 |
52 | /@types/chai/4.3.0:
53 | resolution: {integrity: sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==}
54 | dev: true
55 |
56 | /@types/node/17.0.23:
57 | resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==}
58 | dev: true
59 |
60 | /@types/prop-types/15.7.5:
61 | resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
62 | dev: true
63 |
64 | /@types/react-test-renderer/17.0.1:
65 | resolution: {integrity: sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==}
66 | dependencies:
67 | '@types/react': 18.0.0
68 | dev: true
69 |
70 | /@types/react/18.0.0:
71 | resolution: {integrity: sha512-7+K7zEQYu7NzOwQGLR91KwWXXDzmTFODRVizJyIALf6RfLv2GDpqpknX64pvRVILXCpXi7O/pua8NGk44dLvJw==}
72 | dependencies:
73 | '@types/prop-types': 15.7.5
74 | '@types/scheduler': 0.16.2
75 | csstype: 3.0.11
76 | dev: true
77 |
78 | /@types/scheduler/0.16.2:
79 | resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
80 | dev: true
81 |
82 | /any-promise/1.3.0:
83 | resolution: {integrity: sha1-q8av7tzqUugJzcA3au0845Y10X8=}
84 | dev: true
85 |
86 | /anymatch/3.1.2:
87 | resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
88 | engines: {node: '>= 8'}
89 | dependencies:
90 | normalize-path: 3.0.0
91 | picomatch: 2.3.1
92 | dev: true
93 |
94 | /array-union/2.1.0:
95 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
96 | engines: {node: '>=8'}
97 | dev: true
98 |
99 | /assertion-error/1.1.0:
100 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
101 | dev: true
102 |
103 | /balanced-match/1.0.2:
104 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
105 | dev: true
106 |
107 | /binary-extensions/2.2.0:
108 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
109 | engines: {node: '>=8'}
110 | dev: true
111 |
112 | /brace-expansion/1.1.11:
113 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
114 | dependencies:
115 | balanced-match: 1.0.2
116 | concat-map: 0.0.1
117 | dev: true
118 |
119 | /braces/3.0.2:
120 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
121 | engines: {node: '>=8'}
122 | dependencies:
123 | fill-range: 7.0.1
124 | dev: true
125 |
126 | /bundle-require/3.0.4_esbuild@0.14.34:
127 | resolution: {integrity: sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A==}
128 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
129 | peerDependencies:
130 | esbuild: '>=0.13'
131 | dependencies:
132 | esbuild: 0.14.34
133 | load-tsconfig: 0.2.3
134 | dev: true
135 |
136 | /cac/6.7.12:
137 | resolution: {integrity: sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==}
138 | engines: {node: '>=8'}
139 | dev: true
140 |
141 | /chai/4.3.6:
142 | resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==}
143 | engines: {node: '>=4'}
144 | dependencies:
145 | assertion-error: 1.1.0
146 | check-error: 1.0.2
147 | deep-eql: 3.0.1
148 | get-func-name: 2.0.0
149 | loupe: 2.3.4
150 | pathval: 1.1.1
151 | type-detect: 4.0.8
152 | dev: true
153 |
154 | /check-error/1.0.2:
155 | resolution: {integrity: sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=}
156 | dev: true
157 |
158 | /chokidar/3.5.3:
159 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
160 | engines: {node: '>= 8.10.0'}
161 | dependencies:
162 | anymatch: 3.1.2
163 | braces: 3.0.2
164 | glob-parent: 5.1.2
165 | is-binary-path: 2.1.0
166 | is-glob: 4.0.3
167 | normalize-path: 3.0.0
168 | readdirp: 3.6.0
169 | optionalDependencies:
170 | fsevents: 2.3.2
171 | dev: true
172 |
173 | /commander/4.1.1:
174 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
175 | engines: {node: '>= 6'}
176 | dev: true
177 |
178 | /concat-map/0.0.1:
179 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
180 | dev: true
181 |
182 | /cross-spawn/7.0.3:
183 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
184 | engines: {node: '>= 8'}
185 | dependencies:
186 | path-key: 3.1.1
187 | shebang-command: 2.0.0
188 | which: 2.0.2
189 | dev: true
190 |
191 | /csstype/3.0.11:
192 | resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
193 | dev: true
194 |
195 | /debug/4.3.4:
196 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
197 | engines: {node: '>=6.0'}
198 | peerDependencies:
199 | supports-color: '*'
200 | peerDependenciesMeta:
201 | supports-color:
202 | optional: true
203 | dependencies:
204 | ms: 2.1.2
205 | dev: true
206 |
207 | /deep-eql/3.0.1:
208 | resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==}
209 | engines: {node: '>=0.12'}
210 | dependencies:
211 | type-detect: 4.0.8
212 | dev: true
213 |
214 | /dir-glob/3.0.1:
215 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
216 | engines: {node: '>=8'}
217 | dependencies:
218 | path-type: 4.0.0
219 | dev: true
220 |
221 | /esbuild-android-64/0.14.34:
222 | resolution: {integrity: sha512-XfxcfJqmMYsT/LXqrptzFxmaR3GWzXHDLdFNIhm6S00zPaQF1TBBWm+9t0RZ6LRR7iwH57DPjaOeW20vMqI4Yw==}
223 | engines: {node: '>=12'}
224 | cpu: [x64]
225 | os: [android]
226 | requiresBuild: true
227 | dev: true
228 | optional: true
229 |
230 | /esbuild-android-arm64/0.14.34:
231 | resolution: {integrity: sha512-T02+NXTmSRL1Mc6puz+R9CB54rSPICkXKq6+tw8B6vxZFnCPzbJxgwIX4kcluz9p8nYBjF3+lSilTGWb7+Xgew==}
232 | engines: {node: '>=12'}
233 | cpu: [arm64]
234 | os: [android]
235 | requiresBuild: true
236 | dev: true
237 | optional: true
238 |
239 | /esbuild-darwin-64/0.14.34:
240 | resolution: {integrity: sha512-pLRip2Bh4Ng7Bf6AMgCrSp3pPe/qZyf11h5Qo2mOfJqLWzSVjxrXW+CFRJfrOVP7TCnh/gmZSM2AFdCPB72vtw==}
241 | engines: {node: '>=12'}
242 | cpu: [x64]
243 | os: [darwin]
244 | requiresBuild: true
245 | dev: true
246 | optional: true
247 |
248 | /esbuild-darwin-arm64/0.14.34:
249 | resolution: {integrity: sha512-vpidSJEBxx6lf1NWgXC+DCmGqesJuZ5Y8aQVVsaoO4i8tRXbXb0whChRvop/zd3nfNM4dIl5EXAky0knRX5I6w==}
250 | engines: {node: '>=12'}
251 | cpu: [arm64]
252 | os: [darwin]
253 | requiresBuild: true
254 | dev: true
255 | optional: true
256 |
257 | /esbuild-freebsd-64/0.14.34:
258 | resolution: {integrity: sha512-m0HBjePhe0hAQJgtMRMNV9kMgIyV4/qSnzPx42kRMQBcPhgjAq1JRu4Il26czC+9FgpMbFkUktb07f/Lwnc6CA==}
259 | engines: {node: '>=12'}
260 | cpu: [x64]
261 | os: [freebsd]
262 | requiresBuild: true
263 | dev: true
264 | optional: true
265 |
266 | /esbuild-freebsd-arm64/0.14.34:
267 | resolution: {integrity: sha512-cpRc2B94L1KvMPPYB4D6G39jLqpKlD3noAMY4/e86iXXXkhUYJJEtTuyNFTa9JRpWM0xCAp4mxjHjoIiLuoCLA==}
268 | engines: {node: '>=12'}
269 | cpu: [arm64]
270 | os: [freebsd]
271 | requiresBuild: true
272 | dev: true
273 | optional: true
274 |
275 | /esbuild-linux-32/0.14.34:
276 | resolution: {integrity: sha512-8nQaEaoW7MH/K/RlozJa+lE1ejHIr8fuPIHhc513UebRav7HtXgQvxHQ6VZRUkWtep23M6dd7UqhwO1tMOfzQQ==}
277 | engines: {node: '>=12'}
278 | cpu: [ia32]
279 | os: [linux]
280 | requiresBuild: true
281 | dev: true
282 | optional: true
283 |
284 | /esbuild-linux-64/0.14.34:
285 | resolution: {integrity: sha512-Y3of4qQoLLlAgf042MlrY1P+7PnN9zWj8nVtw9XQG5hcLOZLz7IKpU35oeu7n4wvyaZHwvQqDJ93gRLqdJekcQ==}
286 | engines: {node: '>=12'}
287 | cpu: [x64]
288 | os: [linux]
289 | requiresBuild: true
290 | dev: true
291 | optional: true
292 |
293 | /esbuild-linux-arm/0.14.34:
294 | resolution: {integrity: sha512-9lpq1NcJqssAF7alCO6zL3gvBVVt/lKw4oetUM7OgNnRX0OWpB+ZIO9FwCrSj/dMdmgDhPLf+119zB8QxSMmAg==}
295 | engines: {node: '>=12'}
296 | cpu: [arm]
297 | os: [linux]
298 | requiresBuild: true
299 | dev: true
300 | optional: true
301 |
302 | /esbuild-linux-arm64/0.14.34:
303 | resolution: {integrity: sha512-IlWaGtj9ir7+Nrume1DGcyzBDlK8GcnJq0ANKwcI9pVw8tqr+6GD0eqyF9SF1mR8UmAp+odrx1H5NdR2cHdFHA==}
304 | engines: {node: '>=12'}
305 | cpu: [arm64]
306 | os: [linux]
307 | requiresBuild: true
308 | dev: true
309 | optional: true
310 |
311 | /esbuild-linux-mips64le/0.14.34:
312 | resolution: {integrity: sha512-k3or+01Rska1AjUyNjA4buEwB51eyN/xPQAoOx1CjzAQC3l8rpjUDw55kXyL63O/1MUi4ISvtNtl8gLwdyEcxw==}
313 | engines: {node: '>=12'}
314 | cpu: [mips64el]
315 | os: [linux]
316 | requiresBuild: true
317 | dev: true
318 | optional: true
319 |
320 | /esbuild-linux-ppc64le/0.14.34:
321 | resolution: {integrity: sha512-+qxb8M9FfM2CJaVU7GgYpJOHM1ngQOx+/VrtBjb4C8oVqaPcESCeg2anjl+HRZy8VpYc71q/iBYausPPbJ+Keg==}
322 | engines: {node: '>=12'}
323 | cpu: [ppc64]
324 | os: [linux]
325 | requiresBuild: true
326 | dev: true
327 | optional: true
328 |
329 | /esbuild-linux-riscv64/0.14.34:
330 | resolution: {integrity: sha512-Y717ltBdQ5j5sZIHdy1DV9kieo0wMip0dCmVSTceowCPYSn1Cg33Kd6981+F/3b9FDMzNWldZFOBRILViENZSA==}
331 | engines: {node: '>=12'}
332 | cpu: [riscv64]
333 | os: [linux]
334 | requiresBuild: true
335 | dev: true
336 | optional: true
337 |
338 | /esbuild-linux-s390x/0.14.34:
339 | resolution: {integrity: sha512-bDDgYO4LhL4+zPs+WcBkXph+AQoPcQRTv18FzZS0WhjfH8TZx2QqlVPGhmhZ6WidrY+jKthUqO6UhGyIb4MpmA==}
340 | engines: {node: '>=12'}
341 | cpu: [s390x]
342 | os: [linux]
343 | requiresBuild: true
344 | dev: true
345 | optional: true
346 |
347 | /esbuild-netbsd-64/0.14.34:
348 | resolution: {integrity: sha512-cfaFGXdRt0+vHsjNPyF0POM4BVSHPSbhLPe8mppDc7GDDxjIl08mV1Zou14oDWMp/XZMjYN1kWYRSfftiD0vvQ==}
349 | engines: {node: '>=12'}
350 | cpu: [x64]
351 | os: [netbsd]
352 | requiresBuild: true
353 | dev: true
354 | optional: true
355 |
356 | /esbuild-openbsd-64/0.14.34:
357 | resolution: {integrity: sha512-vmy9DxXVnRiI14s8GKuYBtess+EVcDALkbpTqd5jw4XITutIzyB7n4x0Tj5utAkKsgZJB22lLWGekr0ABnSLow==}
358 | engines: {node: '>=12'}
359 | cpu: [x64]
360 | os: [openbsd]
361 | requiresBuild: true
362 | dev: true
363 | optional: true
364 |
365 | /esbuild-sunos-64/0.14.34:
366 | resolution: {integrity: sha512-eNPVatNET1F7tRMhii7goL/eptfxc0ALRjrj9SPFNqp0zmxrehBFD6BaP3R4LjMn6DbMO0jOAnTLFKr8NqcJAA==}
367 | engines: {node: '>=12'}
368 | cpu: [x64]
369 | os: [sunos]
370 | requiresBuild: true
371 | dev: true
372 | optional: true
373 |
374 | /esbuild-windows-32/0.14.34:
375 | resolution: {integrity: sha512-EFhpXyHEcnqWYe2rAHFd8dRw8wkrd9U+9oqcyoEL84GbanAYjiiIjBZsnR8kl0sCQ5w6bLpk7vCEIA2VS32Vcg==}
376 | engines: {node: '>=12'}
377 | cpu: [ia32]
378 | os: [win32]
379 | requiresBuild: true
380 | dev: true
381 | optional: true
382 |
383 | /esbuild-windows-64/0.14.34:
384 | resolution: {integrity: sha512-a8fbl8Ky7PxNEjf1aJmtxdDZj32/hC7S1OcA2ckEpCJRTjiKslI9vAdPpSjrKIWhws4Galpaawy0nB7fjHYf5Q==}
385 | engines: {node: '>=12'}
386 | cpu: [x64]
387 | os: [win32]
388 | requiresBuild: true
389 | dev: true
390 | optional: true
391 |
392 | /esbuild-windows-arm64/0.14.34:
393 | resolution: {integrity: sha512-EYvmKbSa2B3sPnpC28UEu9jBK5atGV4BaVRE7CYGUci2Hlz4AvtV/LML+TcDMT6gBgibnN2gcltWclab3UutMg==}
394 | engines: {node: '>=12'}
395 | cpu: [arm64]
396 | os: [win32]
397 | requiresBuild: true
398 | dev: true
399 | optional: true
400 |
401 | /esbuild/0.14.34:
402 | resolution: {integrity: sha512-QIWdPT/gFF6hCaf4m7kP0cJ+JIuFkdHibI7vVFvu3eJS1HpVmYHWDulyN5WXwbRA0SX/7ZDaJ/1DH8SdY9xOJg==}
403 | engines: {node: '>=12'}
404 | hasBin: true
405 | requiresBuild: true
406 | optionalDependencies:
407 | esbuild-android-64: 0.14.34
408 | esbuild-android-arm64: 0.14.34
409 | esbuild-darwin-64: 0.14.34
410 | esbuild-darwin-arm64: 0.14.34
411 | esbuild-freebsd-64: 0.14.34
412 | esbuild-freebsd-arm64: 0.14.34
413 | esbuild-linux-32: 0.14.34
414 | esbuild-linux-64: 0.14.34
415 | esbuild-linux-arm: 0.14.34
416 | esbuild-linux-arm64: 0.14.34
417 | esbuild-linux-mips64le: 0.14.34
418 | esbuild-linux-ppc64le: 0.14.34
419 | esbuild-linux-riscv64: 0.14.34
420 | esbuild-linux-s390x: 0.14.34
421 | esbuild-netbsd-64: 0.14.34
422 | esbuild-openbsd-64: 0.14.34
423 | esbuild-sunos-64: 0.14.34
424 | esbuild-windows-32: 0.14.34
425 | esbuild-windows-64: 0.14.34
426 | esbuild-windows-arm64: 0.14.34
427 | dev: true
428 |
429 | /execa/5.1.1:
430 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
431 | engines: {node: '>=10'}
432 | dependencies:
433 | cross-spawn: 7.0.3
434 | get-stream: 6.0.1
435 | human-signals: 2.1.0
436 | is-stream: 2.0.1
437 | merge-stream: 2.0.0
438 | npm-run-path: 4.0.1
439 | onetime: 5.1.2
440 | signal-exit: 3.0.7
441 | strip-final-newline: 2.0.0
442 | dev: true
443 |
444 | /fast-glob/3.2.11:
445 | resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==}
446 | engines: {node: '>=8.6.0'}
447 | dependencies:
448 | '@nodelib/fs.stat': 2.0.5
449 | '@nodelib/fs.walk': 1.2.8
450 | glob-parent: 5.1.2
451 | merge2: 1.4.1
452 | micromatch: 4.0.5
453 | dev: true
454 |
455 | /fastq/1.13.0:
456 | resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
457 | dependencies:
458 | reusify: 1.0.4
459 | dev: true
460 |
461 | /fill-range/7.0.1:
462 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
463 | engines: {node: '>=8'}
464 | dependencies:
465 | to-regex-range: 5.0.1
466 | dev: true
467 |
468 | /fs.realpath/1.0.0:
469 | resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=}
470 | dev: true
471 |
472 | /fsevents/2.3.2:
473 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
474 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
475 | os: [darwin]
476 | requiresBuild: true
477 | dev: true
478 | optional: true
479 |
480 | /function-bind/1.1.1:
481 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
482 | dev: true
483 |
484 | /get-func-name/2.0.0:
485 | resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=}
486 | dev: true
487 |
488 | /get-stream/6.0.1:
489 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
490 | engines: {node: '>=10'}
491 | dev: true
492 |
493 | /glob-parent/5.1.2:
494 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
495 | engines: {node: '>= 6'}
496 | dependencies:
497 | is-glob: 4.0.3
498 | dev: true
499 |
500 | /glob/7.1.6:
501 | resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
502 | dependencies:
503 | fs.realpath: 1.0.0
504 | inflight: 1.0.6
505 | inherits: 2.0.4
506 | minimatch: 3.1.2
507 | once: 1.4.0
508 | path-is-absolute: 1.0.1
509 | dev: true
510 |
511 | /globby/11.1.0:
512 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
513 | engines: {node: '>=10'}
514 | dependencies:
515 | array-union: 2.1.0
516 | dir-glob: 3.0.1
517 | fast-glob: 3.2.11
518 | ignore: 5.2.0
519 | merge2: 1.4.1
520 | slash: 3.0.0
521 | dev: true
522 |
523 | /has/1.0.3:
524 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
525 | engines: {node: '>= 0.4.0'}
526 | dependencies:
527 | function-bind: 1.1.1
528 | dev: true
529 |
530 | /human-signals/2.1.0:
531 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
532 | engines: {node: '>=10.17.0'}
533 | dev: true
534 |
535 | /ignore/5.2.0:
536 | resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
537 | engines: {node: '>= 4'}
538 | dev: true
539 |
540 | /inflight/1.0.6:
541 | resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=}
542 | dependencies:
543 | once: 1.4.0
544 | wrappy: 1.0.2
545 | dev: true
546 |
547 | /inherits/2.0.4:
548 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
549 | dev: true
550 |
551 | /is-binary-path/2.1.0:
552 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
553 | engines: {node: '>=8'}
554 | dependencies:
555 | binary-extensions: 2.2.0
556 | dev: true
557 |
558 | /is-core-module/2.8.1:
559 | resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==}
560 | dependencies:
561 | has: 1.0.3
562 | dev: true
563 |
564 | /is-extglob/2.1.1:
565 | resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=}
566 | engines: {node: '>=0.10.0'}
567 | dev: true
568 |
569 | /is-glob/4.0.3:
570 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
571 | engines: {node: '>=0.10.0'}
572 | dependencies:
573 | is-extglob: 2.1.1
574 | dev: true
575 |
576 | /is-number/7.0.0:
577 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
578 | engines: {node: '>=0.12.0'}
579 | dev: true
580 |
581 | /is-stream/2.0.1:
582 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
583 | engines: {node: '>=8'}
584 | dev: true
585 |
586 | /isexe/2.0.0:
587 | resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=}
588 | dev: true
589 |
590 | /joycon/3.1.1:
591 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
592 | engines: {node: '>=10'}
593 | dev: true
594 |
595 | /js-tokens/4.0.0:
596 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
597 | dev: true
598 |
599 | /lilconfig/2.0.5:
600 | resolution: {integrity: sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==}
601 | engines: {node: '>=10'}
602 | dev: true
603 |
604 | /lines-and-columns/1.2.4:
605 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
606 | dev: true
607 |
608 | /load-tsconfig/0.2.3:
609 | resolution: {integrity: sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==}
610 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
611 | dev: true
612 |
613 | /local-pkg/0.4.1:
614 | resolution: {integrity: sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==}
615 | engines: {node: '>=14'}
616 | dev: true
617 |
618 | /loose-envify/1.4.0:
619 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
620 | hasBin: true
621 | dependencies:
622 | js-tokens: 4.0.0
623 | dev: true
624 |
625 | /loupe/2.3.4:
626 | resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==}
627 | dependencies:
628 | get-func-name: 2.0.0
629 | dev: true
630 |
631 | /merge-stream/2.0.0:
632 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
633 | dev: true
634 |
635 | /merge2/1.4.1:
636 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
637 | engines: {node: '>= 8'}
638 | dev: true
639 |
640 | /micromatch/4.0.5:
641 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
642 | engines: {node: '>=8.6'}
643 | dependencies:
644 | braces: 3.0.2
645 | picomatch: 2.3.1
646 | dev: true
647 |
648 | /mimic-fn/2.1.0:
649 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
650 | engines: {node: '>=6'}
651 | dev: true
652 |
653 | /minimatch/3.1.2:
654 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
655 | dependencies:
656 | brace-expansion: 1.1.11
657 | dev: true
658 |
659 | /ms/2.1.2:
660 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
661 | dev: true
662 |
663 | /mz/2.7.0:
664 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
665 | dependencies:
666 | any-promise: 1.3.0
667 | object-assign: 4.1.1
668 | thenify-all: 1.6.0
669 | dev: true
670 |
671 | /nanoid/3.3.2:
672 | resolution: {integrity: sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==}
673 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
674 | hasBin: true
675 | dev: true
676 |
677 | /normalize-path/3.0.0:
678 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
679 | engines: {node: '>=0.10.0'}
680 | dev: true
681 |
682 | /npm-run-path/4.0.1:
683 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
684 | engines: {node: '>=8'}
685 | dependencies:
686 | path-key: 3.1.1
687 | dev: true
688 |
689 | /object-assign/4.1.1:
690 | resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=}
691 | engines: {node: '>=0.10.0'}
692 | dev: true
693 |
694 | /once/1.4.0:
695 | resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
696 | dependencies:
697 | wrappy: 1.0.2
698 | dev: true
699 |
700 | /onetime/5.1.2:
701 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
702 | engines: {node: '>=6'}
703 | dependencies:
704 | mimic-fn: 2.1.0
705 | dev: true
706 |
707 | /path-is-absolute/1.0.1:
708 | resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=}
709 | engines: {node: '>=0.10.0'}
710 | dev: true
711 |
712 | /path-key/3.1.1:
713 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
714 | engines: {node: '>=8'}
715 | dev: true
716 |
717 | /path-parse/1.0.7:
718 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
719 | dev: true
720 |
721 | /path-type/4.0.0:
722 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
723 | engines: {node: '>=8'}
724 | dev: true
725 |
726 | /pathval/1.1.1:
727 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
728 | dev: true
729 |
730 | /picocolors/1.0.0:
731 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
732 | dev: true
733 |
734 | /picomatch/2.3.1:
735 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
736 | engines: {node: '>=8.6'}
737 | dev: true
738 |
739 | /pirates/4.0.5:
740 | resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
741 | engines: {node: '>= 6'}
742 | dev: true
743 |
744 | /postcss-load-config/3.1.4:
745 | resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
746 | engines: {node: '>= 10'}
747 | peerDependencies:
748 | postcss: '>=8.0.9'
749 | ts-node: '>=9.0.0'
750 | peerDependenciesMeta:
751 | postcss:
752 | optional: true
753 | ts-node:
754 | optional: true
755 | dependencies:
756 | lilconfig: 2.0.5
757 | yaml: 1.10.2
758 | dev: true
759 |
760 | /postcss/8.4.12:
761 | resolution: {integrity: sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==}
762 | engines: {node: ^10 || ^12 || >=14}
763 | dependencies:
764 | nanoid: 3.3.2
765 | picocolors: 1.0.0
766 | source-map-js: 1.0.2
767 | dev: true
768 |
769 | /queue-microtask/1.2.3:
770 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
771 | dev: true
772 |
773 | /react-is/18.0.0:
774 | resolution: {integrity: sha512-yUcBYdBBbo3QiPsgYDcfQcIkGZHfxOaoE6HLSnr1sPzMhdyxusbfKOSUbSd/ocGi32dxcj366PsTj+5oggeKKw==}
775 | dev: true
776 |
777 | /react-shallow-renderer/16.15.0_react@18.0.0:
778 | resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==}
779 | peerDependencies:
780 | react: ^16.0.0 || ^17.0.0 || ^18.0.0
781 | dependencies:
782 | object-assign: 4.1.1
783 | react: 18.0.0
784 | react-is: 18.0.0
785 | dev: true
786 |
787 | /react-test-renderer/18.0.0_react@18.0.0:
788 | resolution: {integrity: sha512-SyZTP/FSkwfiKOZuTZiISzsrC8A80KNlQ8PyyoGoOq+VzMAab6Em1POK/CiX3+XyXG6oiJa1C53zYDbdrJu9fw==}
789 | peerDependencies:
790 | react: ^18.0.0
791 | dependencies:
792 | react: 18.0.0
793 | react-is: 18.0.0
794 | react-shallow-renderer: 16.15.0_react@18.0.0
795 | scheduler: 0.21.0
796 | dev: true
797 |
798 | /react/18.0.0:
799 | resolution: {integrity: sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==}
800 | engines: {node: '>=0.10.0'}
801 | dependencies:
802 | loose-envify: 1.4.0
803 | dev: true
804 |
805 | /readdirp/3.6.0:
806 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
807 | engines: {node: '>=8.10.0'}
808 | dependencies:
809 | picomatch: 2.3.1
810 | dev: true
811 |
812 | /resolve-from/5.0.0:
813 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
814 | engines: {node: '>=8'}
815 | dev: true
816 |
817 | /resolve/1.22.0:
818 | resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
819 | hasBin: true
820 | dependencies:
821 | is-core-module: 2.8.1
822 | path-parse: 1.0.7
823 | supports-preserve-symlinks-flag: 1.0.0
824 | dev: true
825 |
826 | /reusify/1.0.4:
827 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
828 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
829 | dev: true
830 |
831 | /rollup/2.70.1:
832 | resolution: {integrity: sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==}
833 | engines: {node: '>=10.0.0'}
834 | hasBin: true
835 | optionalDependencies:
836 | fsevents: 2.3.2
837 | dev: true
838 |
839 | /run-parallel/1.2.0:
840 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
841 | dependencies:
842 | queue-microtask: 1.2.3
843 | dev: true
844 |
845 | /scheduler/0.21.0:
846 | resolution: {integrity: sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==}
847 | dependencies:
848 | loose-envify: 1.4.0
849 | dev: true
850 |
851 | /shebang-command/2.0.0:
852 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
853 | engines: {node: '>=8'}
854 | dependencies:
855 | shebang-regex: 3.0.0
856 | dev: true
857 |
858 | /shebang-regex/3.0.0:
859 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
860 | engines: {node: '>=8'}
861 | dev: true
862 |
863 | /signal-exit/3.0.7:
864 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
865 | dev: true
866 |
867 | /slash/3.0.0:
868 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
869 | engines: {node: '>=8'}
870 | dev: true
871 |
872 | /source-map-js/1.0.2:
873 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
874 | engines: {node: '>=0.10.0'}
875 | dev: true
876 |
877 | /source-map/0.7.3:
878 | resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==}
879 | engines: {node: '>= 8'}
880 | dev: true
881 |
882 | /strip-final-newline/2.0.0:
883 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
884 | engines: {node: '>=6'}
885 | dev: true
886 |
887 | /sucrase/3.21.0:
888 | resolution: {integrity: sha512-FjAhMJjDcifARI7bZej0Bi1yekjWQHoEvWIXhLPwDhC6O4iZ5PtGb86WV56riW87hzpgB13wwBKO9vKAiWu5VQ==}
889 | engines: {node: '>=8'}
890 | hasBin: true
891 | dependencies:
892 | commander: 4.1.1
893 | glob: 7.1.6
894 | lines-and-columns: 1.2.4
895 | mz: 2.7.0
896 | pirates: 4.0.5
897 | ts-interface-checker: 0.1.13
898 | dev: true
899 |
900 | /supports-preserve-symlinks-flag/1.0.0:
901 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
902 | engines: {node: '>= 0.4'}
903 | dev: true
904 |
905 | /thenify-all/1.6.0:
906 | resolution: {integrity: sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=}
907 | engines: {node: '>=0.8'}
908 | dependencies:
909 | thenify: 3.3.1
910 | dev: true
911 |
912 | /thenify/3.3.1:
913 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
914 | dependencies:
915 | any-promise: 1.3.0
916 | dev: true
917 |
918 | /tinypool/0.1.2:
919 | resolution: {integrity: sha512-fvtYGXoui2RpeMILfkvGIgOVkzJEGediv8UJt7TxdAOY8pnvUkFg/fkvqTfXG9Acc9S17Cnn1S4osDc2164guA==}
920 | engines: {node: '>=14.0.0'}
921 | dev: true
922 |
923 | /tinyspy/0.3.2:
924 | resolution: {integrity: sha512-2+40EP4D3sFYy42UkgkFFB+kiX2Tg3URG/lVvAZFfLxgGpnWl5qQJuBw1gaLttq8UOS+2p3C0WrhJnQigLTT2Q==}
925 | engines: {node: '>=14.0.0'}
926 | dev: true
927 |
928 | /to-regex-range/5.0.1:
929 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
930 | engines: {node: '>=8.0'}
931 | dependencies:
932 | is-number: 7.0.0
933 | dev: true
934 |
935 | /tree-kill/1.2.2:
936 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
937 | hasBin: true
938 | dev: true
939 |
940 | /ts-interface-checker/0.1.13:
941 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
942 | dev: true
943 |
944 | /tsup/5.12.4_typescript@4.6.3:
945 | resolution: {integrity: sha512-uUraITfIj2h6rXAdeaVUYrZ2Is9joLFyEGZN5mGAke874JojCizb2MCUcE0wGdcERtyob5mbbFUKkMgal8SlFw==}
946 | hasBin: true
947 | peerDependencies:
948 | typescript: ^4.1.0
949 | peerDependenciesMeta:
950 | typescript:
951 | optional: true
952 | dependencies:
953 | bundle-require: 3.0.4_esbuild@0.14.34
954 | cac: 6.7.12
955 | chokidar: 3.5.3
956 | debug: 4.3.4
957 | esbuild: 0.14.34
958 | execa: 5.1.1
959 | globby: 11.1.0
960 | joycon: 3.1.1
961 | postcss-load-config: 3.1.4
962 | resolve-from: 5.0.0
963 | rollup: 2.70.1
964 | source-map: 0.7.3
965 | sucrase: 3.21.0
966 | tree-kill: 1.2.2
967 | typescript: 4.6.3
968 | transitivePeerDependencies:
969 | - postcss
970 | - supports-color
971 | - ts-node
972 | dev: true
973 |
974 | /type-detect/4.0.8:
975 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
976 | engines: {node: '>=4'}
977 | dev: true
978 |
979 | /typescript/4.6.3:
980 | resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==}
981 | engines: {node: '>=4.2.0'}
982 | hasBin: true
983 | dev: true
984 |
985 | /vite/2.9.1:
986 | resolution: {integrity: sha512-vSlsSdOYGcYEJfkQ/NeLXgnRv5zZfpAsdztkIrs7AZHV8RCMZQkwjo4DS5BnrYTqoWqLoUe1Cah4aVO4oNNqCQ==}
987 | engines: {node: '>=12.2.0'}
988 | hasBin: true
989 | peerDependencies:
990 | less: '*'
991 | sass: '*'
992 | stylus: '*'
993 | peerDependenciesMeta:
994 | less:
995 | optional: true
996 | sass:
997 | optional: true
998 | stylus:
999 | optional: true
1000 | dependencies:
1001 | esbuild: 0.14.34
1002 | postcss: 8.4.12
1003 | resolve: 1.22.0
1004 | rollup: 2.70.1
1005 | optionalDependencies:
1006 | fsevents: 2.3.2
1007 | dev: true
1008 |
1009 | /vitest/0.9.2:
1010 | resolution: {integrity: sha512-XgR42njw350OxBfKD4MK0cNIzgQrhSUKJq9sgbgRR+bD8GonPCyjpFFmPejptaiEyjmQ2FXpEvFHN37b9X2HJA==}
1011 | engines: {node: '>=v14.16.0'}
1012 | hasBin: true
1013 | peerDependencies:
1014 | '@vitest/ui': '*'
1015 | c8: '*'
1016 | happy-dom: '*'
1017 | jsdom: '*'
1018 | peerDependenciesMeta:
1019 | '@vitest/ui':
1020 | optional: true
1021 | c8:
1022 | optional: true
1023 | happy-dom:
1024 | optional: true
1025 | jsdom:
1026 | optional: true
1027 | dependencies:
1028 | '@types/chai': 4.3.0
1029 | '@types/chai-subset': 1.3.3
1030 | chai: 4.3.6
1031 | local-pkg: 0.4.1
1032 | tinypool: 0.1.2
1033 | tinyspy: 0.3.2
1034 | vite: 2.9.1
1035 | transitivePeerDependencies:
1036 | - less
1037 | - sass
1038 | - stylus
1039 | dev: true
1040 |
1041 | /which/2.0.2:
1042 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1043 | engines: {node: '>= 8'}
1044 | hasBin: true
1045 | dependencies:
1046 | isexe: 2.0.0
1047 | dev: true
1048 |
1049 | /wrappy/1.0.2:
1050 | resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
1051 | dev: true
1052 |
1053 | /yaml/1.10.2:
1054 | resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
1055 | engines: {node: '>= 6'}
1056 | dev: true
1057 |
--------------------------------------------------------------------------------
/screenshots/bare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/bare.png
--------------------------------------------------------------------------------
/screenshots/changed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/changed.png
--------------------------------------------------------------------------------
/screenshots/cli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/cli.png
--------------------------------------------------------------------------------
/screenshots/deduplication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/deduplication.png
--------------------------------------------------------------------------------
/screenshots/info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/info.png
--------------------------------------------------------------------------------
/screenshots/life-cycle-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/life-cycle-events.png
--------------------------------------------------------------------------------
/screenshots/markdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/markdown.png
--------------------------------------------------------------------------------
/screenshots/message.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/message.gif
--------------------------------------------------------------------------------
/screenshots/return-value.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/return-value.png
--------------------------------------------------------------------------------
/screenshots/trace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuding/tilg/935729825b64015b4a344bc09ed16206e8826dc7/screenshots/trace.png
--------------------------------------------------------------------------------
/src/development.ts:
--------------------------------------------------------------------------------
1 | // Created by Shu Ding (shud.in).
2 |
3 | import React, { useState, useEffect, useLayoutEffect, useRef } from 'react'
4 |
5 | function inIframe() {
6 | try {
7 | return window.self !== window.top
8 | } catch (e) {
9 | return true
10 | }
11 | }
12 |
13 | const IS_BROWSER =
14 | typeof window !== 'undefined' &&
15 | window.navigator.product === 'Gecko'
16 | const IS_UNSUPPORTED_CONSOLE =
17 | IS_BROWSER &&
18 | /(\.stackblitz\.io|\.csb\.app)$/.test(location.host) &&
19 | inIframe()
20 |
21 | /**
22 | * This function is a `console.log` formatter with a subset of Markdown syntax
23 | * support, for both browser and CLI:
24 | */
25 | const SEPARATOR = /[-–—!$%^&*()_+|~=`{}\[\]:\/\\"'“”‘’;<>?,.@#\s\n\t\r]$/
26 | function md(strings, args = [], hints = {}, trace = '') {
27 | const disableStyling =
28 | IS_UNSUPPORTED_CONSOLE &&
29 | args.some((arg) => {
30 | return typeof arg === 'function' || (arg && typeof arg === 'object')
31 | })
32 |
33 | let tokens: Record = {}
34 | let formatted = ''
35 | let char = ''
36 |
37 | const result = []
38 | const styles = []
39 |
40 | function setStylesAndFormatted(
41 | type: string,
42 | value: string,
43 | tokenType: boolean
44 | ) {
45 | if (!disableStyling) {
46 | if (IS_BROWSER) {
47 | if (formatted.endsWith('%c')) {
48 | styles[styles.length - 1] += value
49 | } else {
50 | formatted += '%c'
51 | styles.push(value)
52 | }
53 | } else {
54 | formatted += value
55 | }
56 | }
57 | tokens[type] = tokenType
58 | char = undefined
59 | }
60 |
61 | function checkNextOrPrev(value: string | undefined) {
62 | return typeof value === 'undefined' || SEPARATOR.test(value)
63 | }
64 |
65 | function process(
66 | type: string,
67 | open: string,
68 | close: string,
69 | next?: string,
70 | prev?: string
71 | ) {
72 | if (tokens[type] && checkNextOrPrev(next)) {
73 | setStylesAndFormatted(type, close, false)
74 | } else if (!tokens[type] && checkNextOrPrev(prev)) {
75 | setStylesAndFormatted(type, open, true)
76 | } else {
77 | char = type
78 | }
79 | }
80 |
81 | for (let i = 0; i < strings.length; i++) {
82 | formatted = ''
83 | let prev = undefined
84 | const str = strings[i]
85 |
86 | for (let j = 0; j < str.length; j++) {
87 | char = str[j]
88 |
89 | if (char === '*') {
90 | if (str[j + 1] === '*') {
91 | j++
92 | process(
93 | '**',
94 | IS_BROWSER ? 'font-weight: bold;' : '\u001B[1m',
95 | IS_BROWSER ? 'font-weight: normal;' : '\u001B[22m',
96 | str[j + 1],
97 | prev
98 | )
99 | } else {
100 | process(
101 | '*',
102 | IS_BROWSER ? 'font-style: italic;' : '\u001B[3m',
103 | IS_BROWSER ? 'font-style: normal;' : '\u001B[23m',
104 | str[j + 1],
105 | prev
106 | )
107 | }
108 | } else if (char === '_') {
109 | if (str[j + 1] === '_') {
110 | j++
111 | process(
112 | '__',
113 | IS_BROWSER ? 'font-weight: bold;' : '\u001B[1m',
114 | IS_BROWSER ? 'font-weight: normal;' : '\u001B[22m',
115 | str[j + 1],
116 | prev
117 | )
118 | } else {
119 | process(
120 | '_',
121 | IS_BROWSER ? 'font-style: italic;' : '\u001B[3m',
122 | IS_BROWSER ? 'font-style: normal;' : '\u001B[23m',
123 | str[j + 1],
124 | prev
125 | )
126 | }
127 | } else if (char === '`') {
128 | process(
129 | '`',
130 | IS_BROWSER
131 | ? 'background: hsla(0,0%,70%,.3); border-radius:3px; padding: 0 2px;'
132 | : '\u001B[96m\u001B[1m',
133 | IS_BROWSER ? 'background: unset;' : '\u001B[39m\u001B[22m',
134 | str[j + 1],
135 | prev
136 | )
137 | }
138 |
139 | prev = char
140 | if (typeof char !== 'undefined') {
141 | formatted += char
142 | }
143 | }
144 |
145 | const hasPrevSlot = i > 0
146 | const hasNextSlot = i < args.length
147 | if (disableStyling) {
148 | if (hasNextSlot && formatted.endsWith(' ')) {
149 | formatted = formatted.slice(0, -1)
150 | }
151 | if (hasPrevSlot && formatted.startsWith(' ')) {
152 | formatted = formatted.slice(1)
153 | }
154 |
155 | if (formatted !== '') {
156 | result.push(formatted)
157 | }
158 |
159 | if (hasNextSlot) {
160 | if (typeof args[i] === 'string') {
161 | result.push(JSON.stringify(args[i]))
162 | } else {
163 | result.push(args[i])
164 | }
165 | }
166 | } else {
167 | if (!result.length) result.push('')
168 | if (hasNextSlot && hints[i]) {
169 | process(
170 | '~',
171 | IS_BROWSER
172 | ? 'text-decoration: underline; text-decoration-color: green; text-decoration-style: wavy; padding-bottom: 1px; text-decoration-skip-ink: none;'
173 | : '',
174 | ''
175 | )
176 | }
177 |
178 | if (formatted !== '') {
179 | if (result.length) {
180 | result[result.length - 1] += formatted
181 | } else {
182 | result.push(formatted)
183 | }
184 | }
185 |
186 | if (hasNextSlot) {
187 | let serialized
188 | if (
189 | args[i] &&
190 | (typeof args[i] === 'object' || typeof args[i] === 'function')
191 | ) {
192 | result[result.length - 1] += '%o'
193 | styles.push(args[i])
194 | } else {
195 | try {
196 | serialized = JSON.stringify(args[i])
197 | } catch (e) {
198 | serialized = '' + args[i]
199 | }
200 | result[result.length - 1] += serialized
201 | }
202 | }
203 |
204 | if (hasNextSlot && hints[i]) {
205 | formatted = ''
206 | process(
207 | '~',
208 | IS_BROWSER ? 'text-decoration: none; padding-bottom: 0;' : '',
209 | ''
210 | )
211 | result[result.length - 1] += formatted
212 | }
213 | }
214 | }
215 |
216 | if (trace) {
217 | if (disableStyling) {
218 | result.push(` (@ ${trace})`)
219 | } else {
220 | if (!result.length) result.push('')
221 | if (IS_BROWSER) {
222 | result[result.length - 1] += `%c(@ ${trace})`
223 | styles.push(
224 | 'color: #999; font-style: italic; font-size: 0.9em; padding-left: 2em;'
225 | )
226 | } else {
227 | result[result.length - 1] += ` \u001B[2m(@ ${trace})\u001B[22m`
228 | }
229 | }
230 | }
231 |
232 | result.push(...styles)
233 |
234 | return result
235 | }
236 |
237 | function log(...args) {
238 | console.log(...args)
239 | }
240 |
241 | /**
242 | * This function gets the name of the component which calls the useTilg hook.
243 | * Returns null if not able to retrieve the information.
244 | */
245 | const components = new WeakMap()
246 | const instances = new Map()
247 | function useTilgCurrentComponentContext() {
248 | const owner = (React as any)
249 | .__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner
250 | .current || { type: { name: 'unknown' }, memoizedProps: undefined }
251 |
252 | const name = owner.type.name
253 | const id = owner.type
254 |
255 | let path = ''
256 | let logPath = ''
257 |
258 | const stack = new Error().stack.split('\n')
259 | for (const line of stack) {
260 | const match = line.match(/^\s*at\s+(.*?)\s+\((.*?)\)$/)
261 | const callerName = match?.[1]
262 | const callerPath = match?.[2]
263 |
264 | if (callerPath) path += callerPath + ','
265 |
266 | if (callerName && !callerName.startsWith('useTilg')) {
267 | logPath = match[2]
268 | }
269 |
270 | if (
271 | callerName &&
272 | !callerName.startsWith('use') &&
273 | !/.+\.use.+/.test(callerName)
274 | ) {
275 | break
276 | }
277 | }
278 |
279 | return [name, owner, id, path, logPath]
280 | }
281 |
282 | export default function useTilg(...inlined: any[]) {
283 | const mark = useState(Math.random())[0]
284 | const [name, owner, id, hookPath, logPath] = useTilgCurrentComponentContext()
285 |
286 | const compute = () => {
287 | let hookId = 0
288 |
289 | if (!components.has(id)) {
290 | components.set(id, [])
291 | }
292 | const hooks = components.get(id)
293 | hookId = hooks.indexOf(hookPath)
294 | if (hookId === -1) {
295 | hookId = hooks.length
296 | hooks.push(hookPath)
297 | }
298 |
299 | const componentName = name ? '`<' + name + '/>`' : 'Component'
300 |
301 | // Only log the life cycle message for the first hook.
302 | if (hookId !== 0) return [componentName, hookId, 0]
303 |
304 | // Which component instance is this hook located in.
305 | if (!instances.has(id)) {
306 | instances.set(id, [])
307 | }
308 | const instanceMarks = instances.get(id)
309 | let index = instanceMarks.indexOf(mark)
310 | if (index === -1) {
311 | index = instanceMarks.length
312 | instanceMarks.push(mark)
313 | }
314 |
315 | return [componentName, hookId, index]
316 | }
317 |
318 | useEffect(() => {
319 | const [componentName, hookId, instanceId] = compute()
320 |
321 | // Only log the life cycle message for the first hook.
322 | if (hookId !== 0) return
323 | const note = instanceId > 0 ? ` (${instanceId + 1})` : ''
324 |
325 | log(...md([`${componentName}${note} mounted.`]))
326 | return () => {
327 | if (!instances.has(id)) {
328 | instances.set(id, [])
329 | }
330 | const instanceMarks = instances.get(id)
331 | let index = instanceMarks.indexOf(mark)
332 | if (index !== -1) {
333 | instanceMarks[index] = instanceMarks[instanceMarks.length - 1]
334 | instanceMarks.length--
335 | }
336 |
337 | if (!components.has(id)) {
338 | components.set(id, [])
339 | }
340 | const hooks = components.get(id)
341 | index = hooks.indexOf(hookPath)
342 | if (index !== -1) {
343 | hooks[index] = hooks[hooks.length - 1]
344 | hooks.length--
345 | }
346 |
347 | log(...md([`${componentName}${note} unmounted.`]))
348 | }
349 | }, [])
350 |
351 | const loggerEffectContent = useRef(null)
352 | loggerEffectContent.current = null
353 | const loggerPrevArgsContent = useRef(null)
354 | const loggerArgsContent = useRef([])
355 | loggerArgsContent.current = []
356 |
357 | useLayoutEffect(() => {
358 | const [componentName, hookId, instanceId] = compute()
359 |
360 | // Only log the life cycle message for the first hook.
361 | if (hookId === 0) {
362 | const note = instanceId > 0 ? ` (${instanceId + 1})` : ''
363 | log(
364 | ...md(
365 | [`${componentName}${note} rendered with props: \``, '`.'],
366 | [owner.memoizedProps]
367 | )
368 | )
369 | }
370 |
371 | let changed = false
372 | let changedHint = {}
373 |
374 | const prev = loggerPrevArgsContent.current
375 | const now = loggerArgsContent.current
376 | if (!prev || prev.length !== now.length) {
377 | // An arg has been removed or added, do nothing.
378 | changed = true
379 | } else {
380 | for (let i = 0; i < prev.length; i++) {
381 | if (prev[i] !== now[i]) {
382 | changed = true
383 | changedHint[i] = true
384 | }
385 | }
386 | }
387 | loggerPrevArgsContent.current = now
388 |
389 | if (changed && loggerEffectContent.current) {
390 | loggerEffectContent.current(changedHint)
391 | }
392 | })
393 |
394 | if (inlined.length > 0) {
395 | loggerArgsContent.current = inlined
396 | loggerEffectContent.current = (hints) => {
397 | const message = md(
398 | inlined.map((_, i) => (i > 0 ? ', ' : '')).concat(''),
399 | inlined,
400 | hints,
401 | logPath
402 | )
403 |
404 | log(...message)
405 | }
406 | return inlined[0]
407 | }
408 |
409 | return function useTilgInner(strings, ...args: any[]) {
410 | loggerEffectContent.current = (hints: Record) => {
411 | const message = strings.length ? md(strings, args, hints, logPath) : ''
412 |
413 | if (message) {
414 | // This is a work around to get rid of the hook count mismatch bug.
415 | log(...message)
416 | }
417 | }
418 |
419 | loggerArgsContent.current = args
420 |
421 | return args[0]
422 | }
423 | }
424 |
--------------------------------------------------------------------------------
/src/production.ts:
--------------------------------------------------------------------------------
1 | export default function useTilg(...args) {
2 | if (args.length) return args[0]
3 | return (_strings, ...innerArgs) => {
4 | if (innerArgs && innerArgs.length) return innerArgs[0]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/basic.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { act, create } from 'react-test-renderer'
3 | import { afterEach, describe, expect, it, vi } from 'vitest'
4 |
5 | import useTilg from '../src/development'
6 |
7 | describe('useTilg', () => {
8 | afterEach(() => {
9 | vi.restoreAllMocks()
10 | })
11 |
12 | it('should log the render, mount and unmount events', async () => {
13 | const logSpy = vi.spyOn(console, 'log')
14 |
15 | function App() {
16 | useTilg()
17 | return null
18 | }
19 | const app = create( )
20 | app.toJSON()
21 | act(() => app.unmount())
22 |
23 | expect(logSpy.mock.calls[0][0]).toEqual(
24 | '\x1B[96m\x1B[1m \x1B[39m\x1B[22m rendered with props: \x1B[96m\x1B[1m%o\x1B[39m\x1B[22m.',
25 | )
26 | expect(logSpy.mock.calls[0][1]).toEqual({})
27 | expect(logSpy.mock.calls[1][0]).toEqual(
28 | '\x1B[96m\x1B[1m \x1B[39m\x1B[22m mounted.',
29 | )
30 | expect(logSpy.mock.calls[2][0]).toEqual(
31 | '\x1B[96m\x1B[1m \x1B[39m\x1B[22m unmounted.',
32 | )
33 | })
34 |
35 | it('should log the message', async () => {
36 | const logSpy = vi.spyOn(console, 'log')
37 |
38 | function App() {
39 | useTilg()`The we we are`
40 | return null
41 | }
42 | const app = create( )
43 | app.toJSON()
44 | act(() => app.unmount())
45 |
46 | expect(logSpy.mock.calls[1][0]).toContain('The we we are')
47 | })
48 |
49 | it('should log number arguments', async () => {
50 | const logSpy = vi.spyOn(console, 'log')
51 |
52 | function App() {
53 | useTilg()`The answer is ${42}`
54 | return null
55 | }
56 | const app = create( )
57 | app.toJSON()
58 | act(() => app.unmount())
59 |
60 | expect(logSpy.mock.calls[1][0]).toContain('The answer is 42')
61 | })
62 |
63 | it('should log string arguments', async () => {
64 | const logSpy = vi.spyOn(console, 'log')
65 |
66 | function App() {
67 | useTilg()`The answer is ${'42'}`
68 | return null
69 | }
70 | const app = create( )
71 | app.toJSON()
72 | act(() => app.unmount())
73 |
74 | expect(logSpy.mock.calls[1][0]).toContain('The answer is "42"')
75 | })
76 |
77 | it('should log object arguments', async () => {
78 | const logSpy = vi.spyOn(console, 'log')
79 |
80 | function App() {
81 | useTilg()`The answer is ${{ answer: 42 }}`
82 | return null
83 | }
84 | const app = create( )
85 | app.toJSON()
86 | act(() => app.unmount())
87 |
88 | expect(logSpy.mock.calls[1][0]).toContain('The answer is %o')
89 | expect(logSpy.mock.calls[1][1]).toEqual({ answer: 42 })
90 | })
91 |
92 | describe('props', () => {
93 | it('should log props', async () => {
94 | const logSpy = vi.spyOn(console, 'log')
95 |
96 | function App({ answer }) {
97 | useTilg()
98 | return answer
99 | }
100 | const app = create( )
101 | app.toJSON()
102 | act(() => app.unmount())
103 |
104 | expect(logSpy.mock.calls[0][1]).toEqual({ answer: 42 })
105 | })
106 |
107 | it('should log props updates', async () => {
108 | const logSpy = vi.spyOn(console, 'log')
109 |
110 | function App({ answer }) {
111 | useTilg()
112 | return answer
113 | }
114 | const app = create( )
115 | app.toJSON()
116 | act(() => app.update( ))
117 | act(() => app.unmount())
118 |
119 | expect(logSpy.mock.calls[0][1]).toEqual({ answer: 42 })
120 | expect(logSpy.mock.calls[2][1]).toEqual({ answer: 43 })
121 | })
122 | })
123 | })
124 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "esModuleInterop": true,
5 | "lib": ["esnext", "dom"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------