@neurodevs/meta-node 0.12.3 → 0.14.0

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.
Files changed (29) hide show
  1. package/build/__tests__/AbstractPackageTest.d.ts +21 -0
  2. package/build/__tests__/AbstractPackageTest.js +62 -1
  3. package/build/__tests__/AbstractPackageTest.js.map +1 -1
  4. package/build/__tests__/impl/TypescriptClassSnippetSuite.test.d.ts +26 -0
  5. package/build/__tests__/impl/TypescriptClassSnippetSuite.test.js +228 -0
  6. package/build/__tests__/impl/TypescriptClassSnippetSuite.test.js.map +1 -0
  7. package/build/__tests__/impl/VscodeSnippetKeybinder.test.d.ts +0 -9
  8. package/build/__tests__/impl/VscodeSnippetKeybinder.test.js +2 -25
  9. package/build/__tests__/impl/VscodeSnippetKeybinder.test.js.map +1 -1
  10. package/build/impl/TypescriptClassSnippetSuite.d.ts +46 -0
  11. package/build/impl/TypescriptClassSnippetSuite.js +239 -0
  12. package/build/impl/TypescriptClassSnippetSuite.js.map +1 -0
  13. package/build/index.d.ts +4 -0
  14. package/build/index.js +8 -1
  15. package/build/index.js.map +1 -1
  16. package/build/scripts/installSnippetSuite.d.ts +1 -0
  17. package/build/scripts/installSnippetSuite.js +17 -0
  18. package/build/scripts/installSnippetSuite.js.map +1 -0
  19. package/build/testDoubles/SnippetSuite/FakeSnippetSuite.d.ts +8 -0
  20. package/build/testDoubles/SnippetSuite/FakeSnippetSuite.js +18 -0
  21. package/build/testDoubles/SnippetSuite/FakeSnippetSuite.js.map +1 -0
  22. package/package.json +1 -1
  23. package/src/__tests__/AbstractPackageTest.ts +51 -1
  24. package/src/__tests__/impl/TypescriptClassSnippetSuite.test.ts +269 -0
  25. package/src/__tests__/impl/VscodeSnippetKeybinder.test.ts +2 -42
  26. package/src/impl/TypescriptClassSnippetSuite.ts +304 -0
  27. package/src/index.ts +8 -0
  28. package/src/scripts/installSnippetSuite.ts +15 -0
  29. package/src/testDoubles/SnippetSuite/FakeSnippetSuite.ts +19 -0
@@ -0,0 +1,269 @@
1
+ import { readFile, writeFile } from 'fs/promises'
2
+ import { test, assert } from '@sprucelabs/test-utils'
3
+ import {
4
+ callsToWriteFile,
5
+ fakeReadFile,
6
+ fakeWriteFile,
7
+ resetCallsToReadFile,
8
+ resetCallsToWriteFile,
9
+ setFakeReadFileResult,
10
+ } from '@neurodevs/fake-node-core'
11
+ import TypescriptClassSnippetSuite, {
12
+ SnippetSuite,
13
+ } from '../../impl/TypescriptClassSnippetSuite'
14
+ import AbstractPackageTest from '../AbstractPackageTest'
15
+
16
+ export default class TypescriptClassSnippetSuiteTest extends AbstractPackageTest {
17
+ private static instance: SnippetSuite
18
+
19
+ protected static async beforeEach() {
20
+ await super.beforeEach()
21
+
22
+ this.setFakeReadFile()
23
+ this.setFakeWriteFile()
24
+ this.setFakeSnippetsFile()
25
+ this.setFakeKeybindingsFile()
26
+
27
+ this.instance = this.TypescriptClassSnippetSuite()
28
+ }
29
+
30
+ @test()
31
+ protected static async createsInstance() {
32
+ assert.isTruthy(this.instance, 'Failed to create instance!')
33
+ }
34
+
35
+ @test()
36
+ protected static async updatesGlobalSnippetsInVscode() {
37
+ await this.instance.install()
38
+
39
+ assert.isEqualDeep(
40
+ callsToWriteFile[0],
41
+ {
42
+ file: this.snippetsPath,
43
+ data: this.updatedSnippetsFile,
44
+ options: undefined,
45
+ },
46
+ 'Written snippets file is incorrect!'
47
+ )
48
+ }
49
+
50
+ @test()
51
+ protected static async doesNotWriteSnippetsFileIfContentsAreIdentical() {
52
+ setFakeReadFileResult(this.snippetsPath, this.updatedSnippetsFile)
53
+
54
+ await this.instance.install()
55
+
56
+ assert.isEqual(
57
+ callsToWriteFile.filter((call) => call.file === this.snippetsPath)
58
+ .length,
59
+ 0,
60
+ 'Should not have written snippets file!'
61
+ )
62
+ }
63
+
64
+ @test()
65
+ protected static async updatesGlobalKeybindingsInVscode() {
66
+ await this.install()
67
+
68
+ assert.isEqualDeep(
69
+ callsToWriteFile[1],
70
+ {
71
+ file: this.keybindingsPath,
72
+ data: this.updatedKeybindingsFile,
73
+ options: undefined,
74
+ },
75
+ 'Written keybindings file is incorrect!'
76
+ )
77
+ }
78
+
79
+ @test()
80
+ protected static async doesNotWriteKeybindingsFileIfContentsAreIdentical() {
81
+ setFakeReadFileResult(this.keybindingsPath, this.updatedKeybindingsFile)
82
+
83
+ await this.install()
84
+
85
+ assert.isEqual(
86
+ callsToWriteFile.filter(
87
+ (call) => call.file === this.keybindingsPath
88
+ ).length,
89
+ 0,
90
+ 'Should not have written keybindings file!'
91
+ )
92
+ }
93
+
94
+ private static async install() {
95
+ return this.instance.install()
96
+ }
97
+
98
+ private static setFakeReadFile() {
99
+ TypescriptClassSnippetSuite.readFile =
100
+ fakeReadFile as unknown as typeof readFile
101
+ resetCallsToReadFile()
102
+ }
103
+
104
+ private static setFakeWriteFile() {
105
+ TypescriptClassSnippetSuite.writeFile =
106
+ fakeWriteFile as unknown as typeof writeFile
107
+ resetCallsToWriteFile()
108
+ }
109
+
110
+ private static get updatedSnippetsFile() {
111
+ const lastBraceIdx = this.originalSnippetsFile.lastIndexOf('}')
112
+ const before = this.originalSnippetsFile.slice(0, lastBraceIdx)
113
+
114
+ return `${before}${this.snippetsBlock}\n}`
115
+ }
116
+
117
+ private static get snippetsBlock() {
118
+ return ` ${this.snippetStartMarker}\n${this.indentedSnippets}\n ${this.snippetEndMarker}`
119
+ }
120
+
121
+ private static readonly snippetStartMarker =
122
+ '// === TYPESCRIPT CLASS SNIPPETS BEGIN ==='
123
+
124
+ private static readonly snippetEndMarker =
125
+ '// === TYPESCRIPT CLASS SNIPPETS END ==='
126
+
127
+ private static get updatedKeybindingsFile() {
128
+ const lastBraceIdx = this.originalKeybindingsFile.lastIndexOf(']')
129
+ const before = this.originalKeybindingsFile.slice(0, lastBraceIdx)
130
+
131
+ return `${before}${this.keybindingBlock}\n]`
132
+ }
133
+
134
+ private static get keybindingBlock() {
135
+ return ` ${this.keybindingStartMarker}\n${this.indentedKeybindings}\n ${this.keybindingEndMarker}`
136
+ }
137
+
138
+ private static readonly keybindingStartMarker =
139
+ '// === TYPESCRIPT CLASS KEYBINDINGS BEGIN ==='
140
+
141
+ private static readonly keybindingEndMarker =
142
+ '// === TYPESCRIPT CLASS KEYBINDINGS END ==='
143
+
144
+ private static readonly snippets = `
145
+ // === PUBLIC ===
146
+ "Public constructor": { "scope": "typescript", "prefix": "public.constructor", "body": ["public constructor() {}"] },
147
+ "Public field": { "scope": "typescript", "prefix": "public.field", "body": ["public newField = undefined"] },
148
+ "Public readonly field": { "scope": "typescript", "prefix": "public.readonly.field", "body": ["public readonly newField = undefined"] },
149
+ "Public getter": { "scope": "typescript", "prefix": "public.getter", "body": ["public get newProperty() { return undefined }"] },
150
+ "Public setter": { "scope": "typescript", "prefix": "public.setter", "body": ["public set newProperty(_value: unknown) {}"] },
151
+ "Public method": { "scope": "typescript", "prefix": "public.method", "body": ["public newMethod() {}"] },
152
+ "Public async method": { "scope": "typescript", "prefix": "public.async.method", "body": ["public async newMethod() {}"] },
153
+ "Public abstract method": { "scope": "typescript", "prefix": "public.abstract.method", "body": ["public abstract newMethod(): unknown"] },
154
+
155
+ // === PROTECTED ===
156
+ "Protected constructor": { "scope": "typescript", "prefix": "protected.constructor", "body": ["protected constructor() {}"] },
157
+ "Protected field": { "scope": "typescript", "prefix": "protected.field", "body": ["protected newField = undefined"] },
158
+ "Protected readonly field": { "scope": "typescript", "prefix": "protected.readonly.field", "body": ["protected readonly newField = undefined"] },
159
+ "Protected getter": { "scope": "typescript", "prefix": "protected.getter", "body": ["protected get newProperty() { return undefined }"] },
160
+ "Protected setter": { "scope": "typescript", "prefix": "protected.setter", "body": ["protected set newProperty(_value: unknown) {}"] },
161
+ "Protected method": { "scope": "typescript", "prefix": "protected.method", "body": ["protected newMethod() {}"] },
162
+ "Protected async method": { "scope": "typescript", "prefix": "protected.async.method", "body": ["protected async newMethod() {}"] },
163
+ "Protected abstract method": { "scope": "typescript", "prefix": "protected.abstract.method", "body": ["protected abstract newMethod(): unknown"] },
164
+
165
+ // === PRIVATE ===
166
+ "Private constructor": { "scope": "typescript", "prefix": "private.constructor", "body": ["private constructor() {}"] },
167
+ "Private field": { "scope": "typescript", "prefix": "private.field", "body": ["private newField = undefined"] },
168
+ "Private readonly field": { "scope": "typescript", "prefix": "private.readonly.field", "body": ["private readonly newField = undefined"] },
169
+ "Private getter": { "scope": "typescript", "prefix": "private.getter", "body": ["private get newProperty() { return undefined }"] },
170
+ "Private setter": { "scope": "typescript", "prefix": "private.setter", "body": ["private set newProperty(_value: unknown) {}"] },
171
+ "Private method": { "scope": "typescript", "prefix": "private.method", "body": ["private newMethod() {}"] },
172
+ "Private async method": { "scope": "typescript", "prefix": "private.async.method", "body": ["private async newMethod() {}"] },
173
+ "Private abstract method": { "scope": "typescript", "prefix": "private.abstract.method", "body": ["private abstract newMethod(): unknown"] },
174
+
175
+ // === PUBLIC STATIC ===
176
+ "Public static field": { "scope": "typescript", "prefix": "public.static.field", "body": ["public static newField = undefined"] },
177
+ "Public static readonly field": { "scope": "typescript", "prefix": "public.static.readonly.field", "body": ["public static readonly newField = undefined"] },
178
+ "Public static getter": { "scope": "typescript", "prefix": "public.static.getter", "body": ["public static get newProperty() { return undefined }"] },
179
+ "Public static setter": { "scope": "typescript", "prefix": "public.static.setter", "body": ["public static set newProperty(_value: unknown) {}"] },
180
+ "Public static method": { "scope": "typescript", "prefix": "public.static.method", "body": ["public static newMethod() {}"] },
181
+ "Public static async method": { "scope": "typescript", "prefix": "public.static.async.method", "body": ["public static async newMethod() {}"] },
182
+
183
+ // === PROTECTED STATIC ===
184
+ "Protected static field": { "scope": "typescript", "prefix": "protected.static.field", "body": ["protected static newField = undefined"] },
185
+ "Protected static readonly field": { "scope": "typescript", "prefix": "protected.static.readonly.field", "body": ["protected static readonly newField = undefined"] },
186
+ "Protected static getter": { "scope": "typescript", "prefix": "protected.static.getter", "body": ["protected static get newProperty() { return undefined }"] },
187
+ "Protected static setter": { "scope": "typescript", "prefix": "protected.static.setter", "body": ["protected static set newProperty(_value: unknown) {}"] },
188
+ "Protected static method": { "scope": "typescript", "prefix": "protected.static.method", "body": ["protected static newMethod() {}"] },
189
+ "Protected static async method": { "scope": "typescript", "prefix": "protected.static.async.method", "body": ["protected static async newMethod() {}"] },
190
+
191
+ // === PRIVATE STATIC ===
192
+ "Private static field": { "scope": "typescript", "prefix": "private.static.field", "body": ["private static newField = undefined"] },
193
+ "Private static readonly field": { "scope": "typescript", "prefix": "private.static.readonly.field", "body": ["private static readonly newField = undefined"] },
194
+ "Private static getter": { "scope": "typescript", "prefix": "private.static.getter", "body": ["private static get newProperty() { return undefined }"] },
195
+ "Private static setter": { "scope": "typescript", "prefix": "private.static.setter", "body": ["private static set newProperty(_value: unknown) {}"] },
196
+ "Private static method": { "scope": "typescript", "prefix": "private.static.method", "body": ["private static newMethod() {}"] },
197
+ "Private static async method": { "scope": "typescript", "prefix": "private.static.async.method", "body": ["private static async newMethod() {}"] }
198
+ `.replace(/^[ \t]+/gm, '')
199
+
200
+ private static readonly indentedSnippets = this.snippets
201
+ .split('\n')
202
+ .map((line) => (line.trim() ? ' ' + line : line))
203
+ .join('\n')
204
+
205
+ private static readonly keybindings = `
206
+ // === PUBLIC (Ctrl+1) ===
207
+ { "key": "ctrl+1 f1", "command": "editor.action.insertSnippet", "args": { "name": "Public constructor" } },
208
+ { "key": "ctrl+1 f2", "command": "editor.action.insertSnippet", "args": { "name": "Public field" } },
209
+ { "key": "ctrl+1 f3", "command": "editor.action.insertSnippet", "args": { "name": "Public readonly field" } },
210
+ { "key": "ctrl+1 f4", "command": "editor.action.insertSnippet", "args": { "name": "Public getter" } },
211
+ { "key": "ctrl+1 f5", "command": "editor.action.insertSnippet", "args": { "name": "Public setter" } },
212
+ { "key": "ctrl+1 f6", "command": "editor.action.insertSnippet", "args": { "name": "Public method" } },
213
+ { "key": "ctrl+1 f7", "command": "editor.action.insertSnippet", "args": { "name": "Public async method" } },
214
+ { "key": "ctrl+1 f8", "command": "editor.action.insertSnippet", "args": { "name": "Public abstract method" } },
215
+
216
+ // === PROTECTED (Ctrl+2) ===
217
+ { "key": "ctrl+2 f1", "command": "editor.action.insertSnippet", "args": { "name": "Protected constructor" } },
218
+ { "key": "ctrl+2 f2", "command": "editor.action.insertSnippet", "args": { "name": "Protected field" } },
219
+ { "key": "ctrl+2 f3", "command": "editor.action.insertSnippet", "args": { "name": "Protected readonly field" } },
220
+ { "key": "ctrl+2 f4", "command": "editor.action.insertSnippet", "args": { "name": "Protected getter" } },
221
+ { "key": "ctrl+2 f5", "command": "editor.action.insertSnippet", "args": { "name": "Protected setter" } },
222
+ { "key": "ctrl+2 f6", "command": "editor.action.insertSnippet", "args": { "name": "Protected method" } },
223
+ { "key": "ctrl+2 f7", "command": "editor.action.insertSnippet", "args": { "name": "Protected async method" } },
224
+ { "key": "ctrl+2 f8", "command": "editor.action.insertSnippet", "args": { "name": "Protected abstract method" } },
225
+
226
+ // === PRIVATE (Ctrl+3) ===
227
+ { "key": "ctrl+3 f1", "command": "editor.action.insertSnippet", "args": { "name": "Private constructor" } },
228
+ { "key": "ctrl+3 f2", "command": "editor.action.insertSnippet", "args": { "name": "Private field" } },
229
+ { "key": "ctrl+3 f3", "command": "editor.action.insertSnippet", "args": { "name": "Private readonly field" } },
230
+ { "key": "ctrl+3 f4", "command": "editor.action.insertSnippet", "args": { "name": "Private getter" } },
231
+ { "key": "ctrl+3 f5", "command": "editor.action.insertSnippet", "args": { "name": "Private setter" } },
232
+ { "key": "ctrl+3 f6", "command": "editor.action.insertSnippet", "args": { "name": "Private method" } },
233
+ { "key": "ctrl+3 f7", "command": "editor.action.insertSnippet", "args": { "name": "Private async method" } },
234
+ { "key": "ctrl+3 f8", "command": "editor.action.insertSnippet", "args": { "name": "Private abstract method" } },
235
+
236
+ // === PUBLIC STATIC (Ctrl+1 Alt) ===
237
+ { "key": "ctrl+1 alt+f2", "command": "editor.action.insertSnippet", "args": { "name": "Public static field" } },
238
+ { "key": "ctrl+1 alt+f3", "command": "editor.action.insertSnippet", "args": { "name": "Public static readonly field" } },
239
+ { "key": "ctrl+1 alt+f4", "command": "editor.action.insertSnippet", "args": { "name": "Public static getter" } },
240
+ { "key": "ctrl+1 alt+f5", "command": "editor.action.insertSnippet", "args": { "name": "Public static setter" } },
241
+ { "key": "ctrl+1 alt+f6", "command": "editor.action.insertSnippet", "args": { "name": "Public static method" } },
242
+ { "key": "ctrl+1 alt+f7", "command": "editor.action.insertSnippet", "args": { "name": "Public static async method" } },
243
+
244
+ // === PROTECTED STATIC (Ctrl+2 Alt) ===
245
+ { "key": "ctrl+2 alt+f2", "command": "editor.action.insertSnippet", "args": { "name": "Protected static field" } },
246
+ { "key": "ctrl+2 alt+f3", "command": "editor.action.insertSnippet", "args": { "name": "Protected static readonly field" } },
247
+ { "key": "ctrl+2 alt+f4", "command": "editor.action.insertSnippet", "args": { "name": "Protected static getter" } },
248
+ { "key": "ctrl+2 alt+f5", "command": "editor.action.insertSnippet", "args": { "name": "Protected static setter" } },
249
+ { "key": "ctrl+2 alt+f6", "command": "editor.action.insertSnippet", "args": { "name": "Protected static method" } },
250
+ { "key": "ctrl+2 alt+f7", "command": "editor.action.insertSnippet", "args": { "name": "Protected static async method" } },
251
+
252
+ // === PRIVATE STATIC (Ctrl+3 Alt) ===
253
+ { "key": "ctrl+3 alt+f2", "command": "editor.action.insertSnippet", "args": { "name": "Private static field" } },
254
+ { "key": "ctrl+3 alt+f3", "command": "editor.action.insertSnippet", "args": { "name": "Private static readonly field" } },
255
+ { "key": "ctrl+3 alt+f4", "command": "editor.action.insertSnippet", "args": { "name": "Private static getter" } },
256
+ { "key": "ctrl+3 alt+f5", "command": "editor.action.insertSnippet", "args": { "name": "Private static setter" } },
257
+ { "key": "ctrl+3 alt+f6", "command": "editor.action.insertSnippet", "args": { "name": "Private static method" } },
258
+ { "key": "ctrl+3 alt+f7", "command": "editor.action.insertSnippet", "args": { "name": "Private static async method" } }
259
+ `.replace(/^[ \t]+/gm, '')
260
+
261
+ private static readonly indentedKeybindings = this.keybindings
262
+ .split('\n')
263
+ .map((line) => (line.trim() ? ' ' + line : line))
264
+ .join('\n')
265
+
266
+ private static TypescriptClassSnippetSuite() {
267
+ return TypescriptClassSnippetSuite.Create()
268
+ }
269
+ }
@@ -11,7 +11,6 @@ import {
11
11
  import VscodeSnippetKeybinder, {
12
12
  SnippetKeybinder,
13
13
  } from '../../impl/VscodeSnippetKeybinder'
14
- import expandHomeDir from '../../scripts/expandHomeDir'
15
14
  import AbstractPackageTest from '../AbstractPackageTest'
16
15
 
17
16
  export default class VscodeSnippetKeybinderTest extends AbstractPackageTest {
@@ -113,40 +112,12 @@ export default class VscodeSnippetKeybinderTest extends AbstractPackageTest {
113
112
  resetCallsToWriteFile()
114
113
  }
115
114
 
116
- private static setFakeSnippetsFile() {
117
- setFakeReadFileResult(
118
- this.snippetsPath,
119
- JSON.stringify(this.originalSnippetsFile, null, 4)
120
- )
121
- }
122
-
123
- private static setFakeKeybindingsFile() {
124
- setFakeReadFileResult(
125
- this.keybindingsPath,
126
- JSON.stringify(this.originalKeybindingsFile, null, 4)
127
- )
128
- }
129
-
130
115
  private static readonly fakeName = `${generateId()}-${generateId()}_${generateId()} ${generateId()}`
131
116
  private static readonly fakePrefix = this.toCommandId(this.fakeName)
132
117
  private static readonly fakeLines = [generateId(), generateId()]
133
118
  private static readonly fakeKeybinding = generateId()
134
119
  private static readonly fakeDescription = generateId()
135
120
 
136
- private static readonly vscodeDir = expandHomeDir(
137
- '~/Library/Application Support/Code/User'
138
- )
139
-
140
- private static get snippetsPath() {
141
- return `${this.vscodeDir}/snippets/custom.code-snippets`
142
- }
143
-
144
- private static readonly originalSnippet = { [generateId()]: {} }
145
-
146
- private static readonly originalSnippetsFile = {
147
- ...this.originalSnippet,
148
- }
149
-
150
121
  private static get updatedSnippetsFile() {
151
122
  return {
152
123
  ...this.originalSnippet,
@@ -161,20 +132,9 @@ export default class VscodeSnippetKeybinderTest extends AbstractPackageTest {
161
132
 
162
133
  private static readonly originalSnippetsFileCommented = `
163
134
  // ${generateId()}
164
- ${JSON.stringify(this.originalSnippetsFile, null, 4)}
135
+ ${JSON.stringify(this.originalSnippets, null, 4)}
165
136
  `
166
137
 
167
- private static get keybindingsPath() {
168
- return `${this.vscodeDir}/keybindings.json`
169
- }
170
-
171
- private static readonly originalKeybinding = {
172
- key: generateId(),
173
- command: generateId(),
174
- }
175
-
176
- private static readonly originalKeybindingsFile = [this.originalKeybinding]
177
-
178
138
  private static readonly updatedKeybindingsFile = [
179
139
  this.originalKeybinding,
180
140
  {
@@ -189,7 +149,7 @@ export default class VscodeSnippetKeybinderTest extends AbstractPackageTest {
189
149
 
190
150
  private static readonly originalKeybindingsFileCommented = `
191
151
  // ${generateId()}
192
- ${JSON.stringify(this.originalKeybindingsFile, null, 4)}
152
+ ${JSON.stringify(this.originalKeybindings, null, 4)}
193
153
  `
194
154
 
195
155
  private static VscodeSnippetKeybinder() {