@fouaden/snipit 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENCE +21 -0
- package/README.md +141 -0
- package/dist/src/commands/delete.d.ts +2 -0
- package/dist/src/commands/delete.js +20 -0
- package/dist/src/commands/delete.js.map +1 -0
- package/dist/src/commands/get.d.ts +2 -0
- package/dist/src/commands/get.js +27 -0
- package/dist/src/commands/get.js.map +1 -0
- package/dist/src/commands/list.d.ts +2 -0
- package/dist/src/commands/list.js +36 -0
- package/dist/src/commands/list.js.map +1 -0
- package/dist/src/commands/save.d.ts +2 -0
- package/dist/src/commands/save.js +52 -0
- package/dist/src/commands/save.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +17 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/types/index.d.ts +11 -0
- package/dist/src/types/index.js +2 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/utils/storage.d.ts +7 -0
- package/dist/src/utils/storage.js +57 -0
- package/dist/src/utils/storage.js.map +1 -0
- package/dist/test/save.test.d.ts +1 -0
- package/dist/test/save.test.js +141 -0
- package/dist/test/save.test.js.map +1 -0
- package/dist/test/storage.test.d.ts +1 -0
- package/dist/test/storage.test.js +288 -0
- package/dist/test/storage.test.js.map +1 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +16 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +38 -12
package/LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Fouad Adeniran
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# snipit 📎
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>A snappy personal code snippet manager for your terminal.</strong><br>
|
|
5
|
+
Save, search, and copy snippets without leaving your workflow.
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js-18%2B-339933?logo=node.js" alt="Node.js 18+"></a>
|
|
10
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="#install">Install</a> •
|
|
18
|
+
<a href="#quick-start">Quick Start</a> •
|
|
19
|
+
<a href="#commands">Commands</a> •
|
|
20
|
+
<a href="#data">Data</a> •
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Why snipit?
|
|
26
|
+
|
|
27
|
+
You know that one-liner you Google every time? The `tsconfig` you copy from an old project? The regex you always lose?
|
|
28
|
+
|
|
29
|
+
snipit stores them locally, tagged and searchable, ready to copy to your clipboard in one command. No account, no sync, no internet — just a JSON file on your machine.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm i -g @fouaden/snipit
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
**Requirements**: Node.js 18+
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Note!
|
|
45
|
+
macOS/Linux: If you get an EACCES permission error, run
|
|
46
|
+
``` bash
|
|
47
|
+
sudo npm install -g @fouaden/snipit # This happens when npm's global folder is owned by root.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# save a snippet
|
|
54
|
+
snipit save "kill port 3000" --code "lsof -ti:3000 | xargs kill" --tag node
|
|
55
|
+
|
|
56
|
+
# find it
|
|
57
|
+
snipit list --tag node
|
|
58
|
+
|
|
59
|
+
# grab it
|
|
60
|
+
snipit get "kill port 3000" --copy
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Commands
|
|
66
|
+
|
|
67
|
+
### Save a snippet
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# inline code
|
|
71
|
+
snipit save "kill port 3000" --code "lsof -ti:3000 | xargs kill" --tag node
|
|
72
|
+
|
|
73
|
+
# from a file — reads the file content and stores it
|
|
74
|
+
snipit save "tsconfig base" --file ./tsconfig.json --tag ts
|
|
75
|
+
|
|
76
|
+
# multiline — opens your default editor
|
|
77
|
+
snipit save "async fetch" --tag js
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Tags are comma-separated and optional:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
snipit save "docker prune" --code "docker system prune -af" --tag "docker,ops"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### List snippets
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
snipit list # all snippets
|
|
90
|
+
snipit list --tag ts # filter by tag
|
|
91
|
+
snipit list --search "docker" # search by title
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Get a snippet
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
snipit get "kill port 3000" # prints to terminal
|
|
98
|
+
snipit get "kill port 3000" --copy # prints + copies to clipboard
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Since output goes to stdout, you can pipe it too:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
snipit get "tsconfig base" > tsconfig.json
|
|
105
|
+
snipit get "nginx config" | ssh user@server "cat > /etc/nginx/nginx.conf"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Delete a snippet
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
snipit delete 3 # use the ID shown in snipit list
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Data
|
|
117
|
+
|
|
118
|
+
Snippets are stored at `~/.snipit/snippets.json` as a plain JSON file. No database, no server, no account.
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"snippets": [
|
|
123
|
+
{
|
|
124
|
+
"id": 1,
|
|
125
|
+
"title": "kill port 3000",
|
|
126
|
+
"code": "lsof -ti:3000 | xargs kill",
|
|
127
|
+
"tags": ["node"],
|
|
128
|
+
"created": "2026-02-25"
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
"lastId": 1
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
You can back it up, sync it with Dropbox, or commit it to a private repo — it's just a file.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { deleteById } from '../utils/storage.js';
|
|
4
|
+
export const deleteCommand = new Command('delete')
|
|
5
|
+
.description('Delete a snippet by ID')
|
|
6
|
+
.argument('<id>', 'The ID of the snippet to delete (get IDs from snipit list)')
|
|
7
|
+
.action((idArg) => {
|
|
8
|
+
const id = parseInt(idArg, 10);
|
|
9
|
+
if (isNaN(id)) {
|
|
10
|
+
console.error(pc.red('✖ ID must be a number. Run snipit list to find IDs.'));
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const deleted = deleteById(id);
|
|
14
|
+
if (!deleted) {
|
|
15
|
+
console.error(pc.red(`✖ No snippet found with ID #${id}`));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
console.log(pc.green(`✔ Deleted snippet #${id}`));
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,wBAAwB,CAAC;KACrC,QAAQ,CAAC,MAAM,EAAE,4DAA4D,CAAC;KAC9E,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE;IACxB,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAE/B,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAE/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import clipboard from "clipboardy";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import { getByTitle } from "../utils/storage.js";
|
|
5
|
+
export const getCommand = new Command("get")
|
|
6
|
+
.description("Retrieve a snippet by title")
|
|
7
|
+
.argument("<title>", "The title of the snippet to retrive")
|
|
8
|
+
.option("--copy", "Copy the Code to clipboard automatically")
|
|
9
|
+
.action(async (title, options) => {
|
|
10
|
+
const snippet = getByTitle(title);
|
|
11
|
+
if (!snippet) {
|
|
12
|
+
console.error(pc.red(`✖ No snippet found with title "${title}"`));
|
|
13
|
+
console.error(pc.dim(" Run snipit list to see all saved snippets"));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
console.log("");
|
|
17
|
+
console.log(pc.dim(` # ${snippet.title}`));
|
|
18
|
+
console.log("");
|
|
19
|
+
console.log(snippet.code);
|
|
20
|
+
console.log("");
|
|
21
|
+
if (options.copy) {
|
|
22
|
+
await clipboard.write(snippet.code);
|
|
23
|
+
console.log(pc.green(" ✔ Copied to clipboard"));
|
|
24
|
+
console.log("");
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=get.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/commands/get.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,6BAA6B,CAAC;KAC1C,QAAQ,CAAC,SAAS,EAAE,qCAAqC,CAAC;KAC1D,MAAM,CAAC,QAAQ,EAAE,0CAA0C,CAAC;KAC5D,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,OAA2B,EAAE,EAAE;IAC3D,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAElC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,kCAAkC,KAAK,GAAG,CAAC,CAAC,CAAC;QAClE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { getAll } from '../utils/storage.js';
|
|
4
|
+
export const listCommand = new Command('list')
|
|
5
|
+
.description('List all saved snippets')
|
|
6
|
+
.option('-t, --tag <tag>', 'Filter by tag')
|
|
7
|
+
.option('-s, --search <query>', 'Search snippet titles')
|
|
8
|
+
.action((options) => {
|
|
9
|
+
let snippets = getAll();
|
|
10
|
+
if (options.tag) {
|
|
11
|
+
snippets = snippets.filter((s) => s.tags.includes(options.tag.toLowerCase()));
|
|
12
|
+
}
|
|
13
|
+
if (options.search) {
|
|
14
|
+
const q = options.search.toLowerCase();
|
|
15
|
+
snippets = snippets.filter((s) => s.title.toLowerCase().includes(q));
|
|
16
|
+
}
|
|
17
|
+
if (snippets.length === 0) {
|
|
18
|
+
const hint = options.tag
|
|
19
|
+
? `No snippets found with tag "${options.tag}"`
|
|
20
|
+
: 'No snippets yet. Use snipit save to add one.';
|
|
21
|
+
console.log(pc.yellow(`\n ${hint}\n`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
console.log('');
|
|
25
|
+
for (const s of snippets) {
|
|
26
|
+
const id = pc.bold(pc.cyan(` #${s.id}`));
|
|
27
|
+
const title = pc.white(` ${s.title}`);
|
|
28
|
+
const tags = s.tags.length > 0 ? pc.green(s.tags.join(', ')) + pc.dim(' · ') : '';
|
|
29
|
+
const date = pc.dim(s.createdAt);
|
|
30
|
+
console.log(id);
|
|
31
|
+
console.log(title);
|
|
32
|
+
console.log(` ${tags}${date}`);
|
|
33
|
+
console.log('');
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC;KAC1C,MAAM,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;KACvD,MAAM,CAAC,CAAC,OAA0C,EAAE,EAAE;IACrD,IAAI,QAAQ,GAAc,MAAM,EAAE,CAAC;IAEnC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAI,CAAC,WAAW,EAAE,CAAC,CAC5C,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACvC,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG;YACtB,CAAC,CAAC,+BAA+B,OAAO,CAAC,GAAG,GAAG;YAC/C,CAAC,CAAC,8CAA8C,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,EAAE,GAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACvC,MAAM,IAAI,GAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,MAAM,IAAI,GAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAElC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { readFileSync, existsSync } from "fs";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import { addSnippet } from "../utils/storage.js";
|
|
5
|
+
import { editor } from "@inquirer/prompts";
|
|
6
|
+
const previewCode = (code) => {
|
|
7
|
+
const lines = code.split("\n");
|
|
8
|
+
const preview = lines.slice(0, 3).join("\n");
|
|
9
|
+
const hasMore = lines.length > 3;
|
|
10
|
+
console.log(pc.dim(preview));
|
|
11
|
+
if (hasMore)
|
|
12
|
+
console.log(pc.dim(` ... +${lines.length - 3} more lines`));
|
|
13
|
+
};
|
|
14
|
+
export const saveCommand = new Command("save")
|
|
15
|
+
.description("Save a new snippet")
|
|
16
|
+
.argument("<title>", "The title of the snippet to save")
|
|
17
|
+
.option("-c, --code <code>", "Inline code string")
|
|
18
|
+
.option("-f, --file <path>", "Read code from a file")
|
|
19
|
+
.option("-t, --tag <tags>", 'Comma-separated tags e.g. "ts,async"')
|
|
20
|
+
.action(async (title, options) => {
|
|
21
|
+
let code;
|
|
22
|
+
if (options.file) {
|
|
23
|
+
if (!existsSync(options.file)) {
|
|
24
|
+
console.error(pc.red(`✖ File not found: ${options.file}`));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
code = readFileSync(options.file, "utf-8").trim();
|
|
28
|
+
}
|
|
29
|
+
else if (options.code) {
|
|
30
|
+
code = options.code.trim();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
code = await editor({
|
|
34
|
+
message: "Write your snippet (saves when you close the editor):",
|
|
35
|
+
});
|
|
36
|
+
if (!code.trim()) {
|
|
37
|
+
console.error(pc.red("✖ Snippet cannot be empty."));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const tags = options.tag
|
|
42
|
+
? options.tag.split(",").map((t) => t.trim().toLowerCase())
|
|
43
|
+
: [];
|
|
44
|
+
const snippet = addSnippet({ title, code, tags });
|
|
45
|
+
console.log(pc.green(`\n✔ Saved "${snippet.title}" as #${snippet.id}`));
|
|
46
|
+
if (tags.length > 0)
|
|
47
|
+
console.log(pc.dim(` Tags: ${tags.join(", ")}`));
|
|
48
|
+
console.log("");
|
|
49
|
+
previewCode(snippet.code);
|
|
50
|
+
console.log("");
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=save.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.js","sourceRoot":"","sources":["../../../src/commands/save.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getCommand } from "./commands/get.js";
|
|
3
|
+
import { saveCommand } from "./commands/save.js";
|
|
4
|
+
import { deleteCommand } from "./commands/delete.js";
|
|
5
|
+
import { listCommand } from "./commands/list.js";
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name('snipit')
|
|
9
|
+
.description('A local snippet manager')
|
|
10
|
+
.version('1.0.0');
|
|
11
|
+
// Commands
|
|
12
|
+
program.addCommand(saveCommand);
|
|
13
|
+
program.addCommand(getCommand);
|
|
14
|
+
program.addCommand(deleteCommand);
|
|
15
|
+
program.addCommand(listCommand);
|
|
16
|
+
program.parse(process.argv);
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,yBAAyB,CAAC;KACtC,OAAO,CAAC,OAAO,CAAC,CAAA;AAGjB,WAAW;AACX,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;AAC9B,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;AAGjC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Snippet, SnipitStore } from "../types/index.js";
|
|
2
|
+
export declare function readStore(): SnipitStore;
|
|
3
|
+
export declare function writeStore(store: SnipitStore): void;
|
|
4
|
+
export declare function getAll(): Snippet[];
|
|
5
|
+
export declare function getByTitle(title: string): Snippet | undefined;
|
|
6
|
+
export declare function addSnippet(snippet: Omit<Snippet, "id" | "createdAt">): Snippet;
|
|
7
|
+
export declare function deleteById(id: number): boolean;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
// ~/.snipit/snippets.json
|
|
5
|
+
const SNIPIT_DIR = join(homedir(), ".snipit");
|
|
6
|
+
const STORE_PATH = join(SNIPIT_DIR, "snippets.json");
|
|
7
|
+
const DEFAULT_STORE = {
|
|
8
|
+
snippets: [],
|
|
9
|
+
lastId: 0,
|
|
10
|
+
};
|
|
11
|
+
function ensureDirExists() {
|
|
12
|
+
if (!existsSync(SNIPIT_DIR)) {
|
|
13
|
+
mkdirSync(SNIPIT_DIR, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function readStore() {
|
|
17
|
+
ensureDirExists();
|
|
18
|
+
if (!existsSync(STORE_PATH)) {
|
|
19
|
+
return { ...DEFAULT_STORE };
|
|
20
|
+
}
|
|
21
|
+
const raw = readFileSync(STORE_PATH, "utf-8");
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
export function writeStore(store) {
|
|
25
|
+
ensureDirExists();
|
|
26
|
+
writeFileSync(STORE_PATH, JSON.stringify(store, null, 2), "utf-8");
|
|
27
|
+
}
|
|
28
|
+
export function getAll() {
|
|
29
|
+
return readStore().snippets;
|
|
30
|
+
}
|
|
31
|
+
export function getByTitle(title) {
|
|
32
|
+
const store = readStore();
|
|
33
|
+
return store.snippets.find((s) => s.title.toLowerCase() === title.toLowerCase());
|
|
34
|
+
}
|
|
35
|
+
export function addSnippet(snippet) {
|
|
36
|
+
const store = readStore();
|
|
37
|
+
const newSnippet = {
|
|
38
|
+
...snippet,
|
|
39
|
+
id: store.lastId + 1,
|
|
40
|
+
createdAt: new Date().toISOString().split("T")[0],
|
|
41
|
+
};
|
|
42
|
+
store.snippets.push(newSnippet);
|
|
43
|
+
store.lastId = newSnippet.id;
|
|
44
|
+
writeStore(store);
|
|
45
|
+
return newSnippet;
|
|
46
|
+
}
|
|
47
|
+
export function deleteById(id) {
|
|
48
|
+
const store = readStore();
|
|
49
|
+
const lastStoreSize = store.snippets.length;
|
|
50
|
+
store.snippets = store.snippets.filter((s) => s.id != id);
|
|
51
|
+
if (store.snippets.length === lastStoreSize) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
writeStore(store);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/utils/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAQ,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,0BAA0B;AAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAErD,MAAM,aAAa,GAAgB;IACjC,QAAQ,EAAE,EAAE;IACZ,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,SAAS,eAAe;IACtB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,eAAe,EAAE,CAAC;IAClB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC;IAC9B,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAkB;IAC3C,eAAe,EAAE,CAAC;IAClB,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,OAAO,SAAS,EAAE,CAAC,QAAQ,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAE1B,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAA0C;IACnE,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAY;QAC1B,GAAG,OAAO;QACV,EAAE,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC;QACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;KAClD,CAAC;IACF,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC;IAC7B,UAAU,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAS;IAChC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC3C,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;IACzD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAA;IAChB,CAAC;IACD,UAAU,CAAC,KAAK,CAAC,CAAA;IACjB,OAAO,IAAI,CAAA;AAEf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { addSnippet } from "../src/utils/storage.js";
|
|
3
|
+
// Mock the fs module
|
|
4
|
+
vi.mock("fs", () => ({
|
|
5
|
+
existsSync: vi.fn(),
|
|
6
|
+
readFileSync: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
// Mock picocolors to return plain strings
|
|
9
|
+
vi.mock("picocolors", () => ({
|
|
10
|
+
default: {
|
|
11
|
+
green: (str) => str,
|
|
12
|
+
red: (str) => str,
|
|
13
|
+
dim: (str) => str,
|
|
14
|
+
},
|
|
15
|
+
green: (str) => str,
|
|
16
|
+
red: (str) => str,
|
|
17
|
+
dim: (str) => str,
|
|
18
|
+
}));
|
|
19
|
+
// Mock the inquirer editor prompt
|
|
20
|
+
vi.mock("@inquirer/prompts", () => ({
|
|
21
|
+
editor: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
// Mock storage module - use a function to properly spread the input
|
|
24
|
+
vi.mock("../src/utils/storage.js", () => ({
|
|
25
|
+
addSnippet: vi.fn((snippet) => ({
|
|
26
|
+
...snippet,
|
|
27
|
+
id: 1,
|
|
28
|
+
createdAt: "2024-01-01",
|
|
29
|
+
})),
|
|
30
|
+
}));
|
|
31
|
+
// Import mocked modules
|
|
32
|
+
import { existsSync, readFileSync } from "fs";
|
|
33
|
+
describe("Save Command", () => {
|
|
34
|
+
let consoleLogSpy;
|
|
35
|
+
let consoleErrorSpy;
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks();
|
|
38
|
+
// Spy on console methods
|
|
39
|
+
consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
40
|
+
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
consoleLogSpy.mockRestore();
|
|
44
|
+
consoleErrorSpy.mockRestore();
|
|
45
|
+
});
|
|
46
|
+
describe("Error handling for file not found", () => {
|
|
47
|
+
it("should error when file path does not exist", async () => {
|
|
48
|
+
// Setup: mock existsSync to return false (file doesn't exist)
|
|
49
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
50
|
+
// Create a new program instance for each test to avoid state issues
|
|
51
|
+
const { saveCommand } = await import("../src/commands/save.js");
|
|
52
|
+
// We need to test the error case by calling the action function directly
|
|
53
|
+
// Since commander exits on error, we test the underlying logic
|
|
54
|
+
const filePath = "/non/existent/file.txt";
|
|
55
|
+
const fileExists = existsSync(filePath);
|
|
56
|
+
expect(fileExists).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
it("should not call addSnippet when file does not exist", async () => {
|
|
59
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
60
|
+
// Verify that when file doesn't exist, addSnippet should not be called
|
|
61
|
+
// The actual error handling happens inside the command action
|
|
62
|
+
const filePath = "/non/existent/file.txt";
|
|
63
|
+
if (!existsSync(filePath)) {
|
|
64
|
+
// This is the error path
|
|
65
|
+
}
|
|
66
|
+
expect(addSnippet).not.toHaveBeenCalled();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("File-based saving", () => {
|
|
70
|
+
it("should read and save code from file", async () => {
|
|
71
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
72
|
+
vi.mocked(readFileSync).mockReturnValue("console.log('hello world')");
|
|
73
|
+
const { readFileSync: rfSync, existsSync: exSync } = await import("fs");
|
|
74
|
+
// Verify mocks are set up correctly
|
|
75
|
+
expect(exSync("/path/to/file.js")).toBe(true);
|
|
76
|
+
expect(rfSync("/path/to/file.js", "utf-8")).toBe("console.log('hello world')");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("addSnippet function", () => {
|
|
80
|
+
it("should add snippet with correct properties", () => {
|
|
81
|
+
const result = addSnippet({
|
|
82
|
+
title: "test-snippet",
|
|
83
|
+
code: "console.log('test')",
|
|
84
|
+
tags: [],
|
|
85
|
+
});
|
|
86
|
+
expect(result).toEqual({
|
|
87
|
+
id: 1,
|
|
88
|
+
title: "test-snippet",
|
|
89
|
+
code: "console.log('test')",
|
|
90
|
+
tags: [],
|
|
91
|
+
createdAt: "2024-01-01",
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
it("should generate id and timestamp", () => {
|
|
95
|
+
const result = addSnippet({
|
|
96
|
+
title: "new-snippet",
|
|
97
|
+
code: "code",
|
|
98
|
+
tags: [],
|
|
99
|
+
});
|
|
100
|
+
expect(result.id).toBeDefined();
|
|
101
|
+
expect(result.createdAt).toBeDefined();
|
|
102
|
+
expect(typeof result.id).toBe("number");
|
|
103
|
+
expect(typeof result.createdAt).toBe("string");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe("Console output verification", () => {
|
|
107
|
+
it("should log success message format", () => {
|
|
108
|
+
// Test that console.log can be called with expected format
|
|
109
|
+
console.log('✔ Saved "test-snippet" as #1');
|
|
110
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('✔ Saved "test-snippet" as #1');
|
|
111
|
+
});
|
|
112
|
+
it("should log error message format", () => {
|
|
113
|
+
console.error("✖ File not found: /path/to/file.js");
|
|
114
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith("✖ File not found: /path/to/file.js");
|
|
115
|
+
});
|
|
116
|
+
it("should log tag information", () => {
|
|
117
|
+
console.log(" Tags: js, ts, async");
|
|
118
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(" Tags: js, ts, async");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe("Tag parsing logic", () => {
|
|
122
|
+
it("should parse comma-separated tags", () => {
|
|
123
|
+
const tagString = "js, ts, async";
|
|
124
|
+
const tags = tagString.split(",").map((t) => t.trim().toLowerCase());
|
|
125
|
+
expect(tags).toEqual(["js", "ts", "async"]);
|
|
126
|
+
});
|
|
127
|
+
it("should handle empty tag string", () => {
|
|
128
|
+
const tagString = "";
|
|
129
|
+
const tags = tagString
|
|
130
|
+
? tagString.split(",").map((t) => t.trim().toLowerCase())
|
|
131
|
+
: [];
|
|
132
|
+
expect(tags).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
it("should trim and lowercase tags", () => {
|
|
135
|
+
const tagString = " JAVASCRIPT , TYPESCRIPT ";
|
|
136
|
+
const tags = tagString.split(",").map((t) => t.trim().toLowerCase());
|
|
137
|
+
expect(tags).toEqual(["javascript", "typescript"]);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
//# sourceMappingURL=save.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.test.js","sourceRoot":"","sources":["../../test/save.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { addSnippet, getAll, getByTitle, deleteById, readStore, writeStore, } from "../src/utils/storage.js";
|
|
3
|
+
// Import mocked modules
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
// Mock the fs module
|
|
6
|
+
vi.mock("fs", () => ({
|
|
7
|
+
existsSync: vi.fn(),
|
|
8
|
+
mkdirSync: vi.fn(),
|
|
9
|
+
readFileSync: vi.fn(),
|
|
10
|
+
writeFileSync: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
// Mock the os module for homedir
|
|
13
|
+
vi.mock("os", () => ({
|
|
14
|
+
homedir: vi.fn(() => "/mocked/home"),
|
|
15
|
+
}));
|
|
16
|
+
// Mock path to avoid issues with ESM
|
|
17
|
+
vi.mock("path", () => ({
|
|
18
|
+
join: (...args) => args.join("/"),
|
|
19
|
+
}));
|
|
20
|
+
describe("Storage Utility", () => {
|
|
21
|
+
let mockStore;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
// Reset mock store before each test
|
|
24
|
+
mockStore = {
|
|
25
|
+
snippets: [],
|
|
26
|
+
lastId: 0,
|
|
27
|
+
};
|
|
28
|
+
// Reset all mocks
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
// Setup default mock implementations
|
|
31
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
32
|
+
vi.mocked(mkdirSync).mockReturnValue(undefined);
|
|
33
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
34
|
+
vi.mocked(writeFileSync).mockReturnValue(undefined);
|
|
35
|
+
});
|
|
36
|
+
describe("addSnippet", () => {
|
|
37
|
+
it("should add a snippet with generated id and timestamp", () => {
|
|
38
|
+
const result = addSnippet({
|
|
39
|
+
title: "Test Snippet",
|
|
40
|
+
code: 'console.log("hello")',
|
|
41
|
+
tags: ["js", "test"],
|
|
42
|
+
});
|
|
43
|
+
// Verify id is generated (should be lastId + 1 = 1)
|
|
44
|
+
expect(result.id).toBe(1);
|
|
45
|
+
// Verify timestamp is in YYYY-MM-DD format
|
|
46
|
+
expect(result.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
|
47
|
+
// Verify the snippet object has all expected properties
|
|
48
|
+
expect(result).toEqual({
|
|
49
|
+
id: 1,
|
|
50
|
+
title: "Test Snippet",
|
|
51
|
+
code: 'console.log("hello")',
|
|
52
|
+
tags: ["js", "test"],
|
|
53
|
+
createdAt: expect.any(String),
|
|
54
|
+
});
|
|
55
|
+
// Verify writeStore was called
|
|
56
|
+
expect(writeFileSync).toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
it("should generate sequential IDs", () => {
|
|
59
|
+
// Mock readFileSync to return updated store after each addSnippet call
|
|
60
|
+
let callCount = 0;
|
|
61
|
+
vi.mocked(readFileSync).mockImplementation(() => {
|
|
62
|
+
callCount++;
|
|
63
|
+
if (callCount === 1) {
|
|
64
|
+
return JSON.stringify({ snippets: [], lastId: 0 });
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return JSON.stringify({
|
|
68
|
+
snippets: [
|
|
69
|
+
{
|
|
70
|
+
id: 1,
|
|
71
|
+
title: "First Snippet",
|
|
72
|
+
code: "code1",
|
|
73
|
+
tags: [],
|
|
74
|
+
createdAt: "2024-01-01",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
lastId: 1,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const snippet1 = addSnippet({
|
|
82
|
+
title: "First Snippet",
|
|
83
|
+
code: "code1",
|
|
84
|
+
tags: [],
|
|
85
|
+
});
|
|
86
|
+
const snippet2 = addSnippet({
|
|
87
|
+
title: "Second Snippet",
|
|
88
|
+
code: "code2",
|
|
89
|
+
tags: [],
|
|
90
|
+
});
|
|
91
|
+
expect(snippet1.id).toBe(1);
|
|
92
|
+
expect(snippet2.id).toBe(2);
|
|
93
|
+
});
|
|
94
|
+
it("should use Omit type - id and createdAt should not be in input", () => {
|
|
95
|
+
// This test verifies the Omit logic by ensuring the function
|
|
96
|
+
// only accepts title, code, and tags (not id or createdAt)
|
|
97
|
+
const input = {
|
|
98
|
+
title: "Test",
|
|
99
|
+
code: "test code",
|
|
100
|
+
tags: ["tag1"],
|
|
101
|
+
};
|
|
102
|
+
// TypeScript should only allow these three properties
|
|
103
|
+
// If Omit is working correctly, we cannot pass id or createdAt
|
|
104
|
+
const result = addSnippet(input);
|
|
105
|
+
// Verify the result has id and createdAt added by the function
|
|
106
|
+
expect(result.id).toBeDefined();
|
|
107
|
+
expect(result.createdAt).toBeDefined();
|
|
108
|
+
expect(result.title).toBe(input.title);
|
|
109
|
+
expect(result.code).toBe(input.code);
|
|
110
|
+
expect(result.tags).toEqual(input.tags);
|
|
111
|
+
});
|
|
112
|
+
it("should handle empty tags array", () => {
|
|
113
|
+
const result = addSnippet({
|
|
114
|
+
title: "No Tags",
|
|
115
|
+
code: "some code",
|
|
116
|
+
tags: [],
|
|
117
|
+
});
|
|
118
|
+
expect(result.tags).toEqual([]);
|
|
119
|
+
});
|
|
120
|
+
it("should write to the store file", () => {
|
|
121
|
+
addSnippet({
|
|
122
|
+
title: "Test",
|
|
123
|
+
code: "code",
|
|
124
|
+
tags: [],
|
|
125
|
+
});
|
|
126
|
+
expect(writeFileSync).toHaveBeenCalledWith("/mocked/home/.snipit/snippets.json", expect.any(String), "utf-8");
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe("getAll", () => {
|
|
130
|
+
it("should return all snippets from store", () => {
|
|
131
|
+
mockStore.snippets = [
|
|
132
|
+
{
|
|
133
|
+
id: 1,
|
|
134
|
+
title: "Snippet 1",
|
|
135
|
+
code: "code1",
|
|
136
|
+
tags: [],
|
|
137
|
+
createdAt: "2024-01-01",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 2,
|
|
141
|
+
title: "Snippet 2",
|
|
142
|
+
code: "code2",
|
|
143
|
+
tags: ["js"],
|
|
144
|
+
createdAt: "2024-01-02",
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
mockStore.lastId = 2;
|
|
148
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
149
|
+
const result = getAll();
|
|
150
|
+
expect(result).toHaveLength(2);
|
|
151
|
+
expect(result[0].title).toBe("Snippet 1");
|
|
152
|
+
expect(result[1].title).toBe("Snippet 2");
|
|
153
|
+
});
|
|
154
|
+
it("should return empty array when no snippets exist", () => {
|
|
155
|
+
mockStore.snippets = [];
|
|
156
|
+
mockStore.lastId = 0;
|
|
157
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
158
|
+
const result = getAll();
|
|
159
|
+
expect(result).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe("getByTitle", () => {
|
|
163
|
+
it("should find snippet by title (case insensitive)", () => {
|
|
164
|
+
mockStore.snippets = [
|
|
165
|
+
{
|
|
166
|
+
id: 1,
|
|
167
|
+
title: "JavaScript Code",
|
|
168
|
+
code: "console.log",
|
|
169
|
+
tags: [],
|
|
170
|
+
createdAt: "2024-01-01",
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
174
|
+
const result = getByTitle("javascript code");
|
|
175
|
+
expect(result).toBeDefined();
|
|
176
|
+
expect(result?.title).toBe("JavaScript Code");
|
|
177
|
+
});
|
|
178
|
+
it("should return undefined for non-existent title", () => {
|
|
179
|
+
mockStore.snippets = [
|
|
180
|
+
{
|
|
181
|
+
id: 1,
|
|
182
|
+
title: "Existing",
|
|
183
|
+
code: "code",
|
|
184
|
+
tags: [],
|
|
185
|
+
createdAt: "2024-01-01",
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
189
|
+
const result = getByTitle("NonExistent");
|
|
190
|
+
expect(result).toBeUndefined();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
describe("deleteById", () => {
|
|
194
|
+
it("should delete snippet by id and return true", () => {
|
|
195
|
+
mockStore.snippets = [
|
|
196
|
+
{
|
|
197
|
+
id: 1,
|
|
198
|
+
title: "To Delete",
|
|
199
|
+
code: "code",
|
|
200
|
+
tags: [],
|
|
201
|
+
createdAt: "2024-01-01",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 2,
|
|
205
|
+
title: "To Keep",
|
|
206
|
+
code: "code",
|
|
207
|
+
tags: [],
|
|
208
|
+
createdAt: "2024-01-02",
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
mockStore.lastId = 2;
|
|
212
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
213
|
+
const result = deleteById(1);
|
|
214
|
+
expect(result).toBe(true);
|
|
215
|
+
expect(writeFileSync).toHaveBeenCalled();
|
|
216
|
+
});
|
|
217
|
+
it("should return false when id does not exist", () => {
|
|
218
|
+
mockStore.snippets = [
|
|
219
|
+
{
|
|
220
|
+
id: 1,
|
|
221
|
+
title: "Existing",
|
|
222
|
+
code: "code",
|
|
223
|
+
tags: [],
|
|
224
|
+
createdAt: "2024-01-01",
|
|
225
|
+
},
|
|
226
|
+
];
|
|
227
|
+
mockStore.lastId = 1;
|
|
228
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
229
|
+
const result = deleteById(999);
|
|
230
|
+
expect(result).toBe(false);
|
|
231
|
+
expect(writeFileSync).not.toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe("readStore", () => {
|
|
235
|
+
it("should return default store when file does not exist", () => {
|
|
236
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
237
|
+
const result = readStore();
|
|
238
|
+
expect(result).toEqual({
|
|
239
|
+
snippets: [],
|
|
240
|
+
lastId: 0,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
it("should parse and return existing store", () => {
|
|
244
|
+
mockStore.snippets = [
|
|
245
|
+
{
|
|
246
|
+
id: 1,
|
|
247
|
+
title: "Test",
|
|
248
|
+
code: "code",
|
|
249
|
+
tags: [],
|
|
250
|
+
createdAt: "2024-01-01",
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
mockStore.lastId = 1;
|
|
254
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
255
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
|
|
256
|
+
const result = readStore();
|
|
257
|
+
expect(result.snippets).toHaveLength(1);
|
|
258
|
+
expect(result.lastId).toBe(1);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
describe("writeStore", () => {
|
|
262
|
+
it("should write store to file", () => {
|
|
263
|
+
const store = {
|
|
264
|
+
snippets: [
|
|
265
|
+
{
|
|
266
|
+
id: 1,
|
|
267
|
+
title: "Test",
|
|
268
|
+
code: "code",
|
|
269
|
+
tags: [],
|
|
270
|
+
createdAt: "2024-01-01",
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
lastId: 1,
|
|
274
|
+
};
|
|
275
|
+
writeStore(store);
|
|
276
|
+
expect(writeFileSync).toHaveBeenCalledWith("/mocked/home/.snipit/snippets.json", JSON.stringify(store, null, 2), "utf-8");
|
|
277
|
+
});
|
|
278
|
+
it("should ensure directory exists before writing", () => {
|
|
279
|
+
// Make sure existsSync returns false so ensureDirExists creates the directory
|
|
280
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
281
|
+
writeStore({ snippets: [], lastId: 0 });
|
|
282
|
+
expect(mkdirSync).toHaveBeenCalledWith("/mocked/home/.snipit", {
|
|
283
|
+
recursive: true,
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
//# sourceMappingURL=storage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.test.js","sourceRoot":"","sources":["../../test/storage.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
export default defineConfig({
|
|
3
|
+
test: {
|
|
4
|
+
globals: true,
|
|
5
|
+
environment: "node",
|
|
6
|
+
include: ["test/**/*.test.ts"],
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: "v8",
|
|
9
|
+
reporter: ["text", "json", "html"],
|
|
10
|
+
},
|
|
11
|
+
typecheck: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=vitest.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE;QACJ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,CAAC,mBAAmB,CAAC;QAC9B,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SACnC;QACD,SAAS,EAAE;YACT,OAAO,EAAE,IAAI;SACd;KACF;CACF,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,32 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fouaden/snipit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A lightweight CLI tool to save, search and tag code snippets locally. Stop re-typing; start snipping.",
|
|
5
5
|
"main": "bin/snipit.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"snipit": "./bin/snipit.js"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/fouadbuilds/snipit.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/fouadbuilds/snipit/issues"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/fouadbuilds/snipit#readme",
|
|
6
19
|
"scripts": {
|
|
7
20
|
"dev": "tsx bin/snipit.js",
|
|
8
21
|
"build": "tsc",
|
|
9
|
-
"start": "node bin/snipit.js"
|
|
22
|
+
"start": "node bin/snipit.js",
|
|
23
|
+
"test": "vitest",
|
|
24
|
+
"test:run": "vitest run",
|
|
25
|
+
"test:ui": "vitest --ui"
|
|
10
26
|
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"keywords": [
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/",
|
|
29
|
+
"bin/"
|
|
30
|
+
],
|
|
31
|
+
"keywords": [
|
|
32
|
+
"cli",
|
|
33
|
+
"snipit",
|
|
34
|
+
"snippet-manager",
|
|
35
|
+
"developer-tools",
|
|
36
|
+
"typescript",
|
|
37
|
+
"productivity",
|
|
38
|
+
"code-snippets",
|
|
39
|
+
"terminal",
|
|
40
|
+
"local-storage",
|
|
41
|
+
"node-cli"
|
|
42
|
+
],
|
|
16
43
|
"author": "fouadbuilds",
|
|
17
44
|
"license": "MIT",
|
|
18
|
-
"type": "module",
|
|
19
45
|
"devDependencies": {
|
|
20
46
|
"@types/node": "^25.3.0",
|
|
21
47
|
"ts-node": "^10.9.2",
|
|
22
48
|
"tsx": "^4.21.0",
|
|
23
|
-
"typescript": "^5.9.3"
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vitest": "^4.0.18"
|
|
24
51
|
},
|
|
25
52
|
"dependencies": {
|
|
26
53
|
"@inquirer/prompts": "^8.3.0",
|
|
27
54
|
"clipboardy": "^5.3.1",
|
|
28
55
|
"commander": "^14.0.3",
|
|
29
|
-
"picocolors": "^1.1.1"
|
|
30
|
-
"vitest": "^4.0.18"
|
|
56
|
+
"picocolors": "^1.1.1"
|
|
31
57
|
}
|
|
32
|
-
}
|
|
58
|
+
}
|