@herb-tools/config 0.8.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.
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ export { Config } from "./config.js"
2
+ export { HerbConfigSchema } from "./config-schema.js"
3
+ export { addHerbExtensionRecommendation, getExtensionsJsonRelativePath } from "./vscode.js"
4
+
5
+ export type {
6
+ HerbConfig,
7
+ HerbConfigOptions,
8
+ LinterConfig,
9
+ FormatterConfig,
10
+ RuleConfig,
11
+ FilesConfig,
12
+ LoadOptions,
13
+ FromObjectOptions,
14
+ ConfigValidationError
15
+ } from "./config.js"
16
+
17
+ export type { VSCodeExtensionsJson } from "./vscode.js"
package/src/merge.ts ADDED
@@ -0,0 +1,47 @@
1
+ type DeepPartial<T> = T extends object ? {
2
+ [P in keyof T]?: DeepPartial<T[P]>;
3
+ } : T;
4
+
5
+ function isObject(item: unknown): item is Record<string, any> {
6
+ return item !== null && typeof item === 'object' && !Array.isArray(item)
7
+ }
8
+
9
+ /**
10
+ * Deep merge two objects
11
+ * @param target - The base object (defaults)
12
+ * @param source - The object to merge in (user config)
13
+ * @returns Merged object
14
+ */
15
+ export function deepMerge<T extends Record<string, any>>(target: T, source: DeepPartial<T>): T {
16
+ const output = { ...target }
17
+
18
+ for (const key in source) {
19
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
20
+ const sourceValue = source[key]
21
+ const targetValue = target[key]
22
+
23
+ if (sourceValue === undefined) {
24
+ continue
25
+ }
26
+
27
+ if (Array.isArray(sourceValue)) {
28
+ if (key === 'include' && Array.isArray(targetValue)) {
29
+ ;(output as any)[key] = [...targetValue, ...sourceValue]
30
+ } else {
31
+ ;(output as any)[key] = [...sourceValue]
32
+ }
33
+ continue
34
+ }
35
+
36
+ if (isObject(sourceValue) && isObject(targetValue)) {
37
+ ;(output as any)[key] = deepMerge(targetValue, sourceValue)
38
+
39
+ continue
40
+ }
41
+
42
+ ;(output as any)[key] = sourceValue
43
+ }
44
+ }
45
+
46
+ return output
47
+ }
package/src/vscode.ts ADDED
@@ -0,0 +1,96 @@
1
+ import { join } from "path"
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs"
3
+
4
+ export interface VSCodeExtensionsJson {
5
+ recommendations?: string[]
6
+ unwantedRecommendations?: string[]
7
+ }
8
+
9
+ const HERB_EXTENSION_ID = "marcoroth.herb-lsp"
10
+ const VSCODE_DIR = ".vscode"
11
+ const EXTENSIONS_FILE = "extensions.json"
12
+
13
+ /**
14
+ * Ensures the .vscode directory exists in the project
15
+ */
16
+ function ensureVSCodeDirectory(projectPath: string): string {
17
+ const vscodeDir = join(projectPath, VSCODE_DIR)
18
+
19
+ if (!existsSync(vscodeDir)) {
20
+ mkdirSync(vscodeDir, { recursive: true })
21
+ }
22
+
23
+ return vscodeDir
24
+ }
25
+
26
+ /**
27
+ * Gets the path to the VSCode extensions.json file
28
+ */
29
+ function getExtensionsJsonPath(projectPath: string): string {
30
+ const vscodeDir = ensureVSCodeDirectory(projectPath)
31
+
32
+ return join(vscodeDir, EXTENSIONS_FILE)
33
+ }
34
+
35
+ /**
36
+ * Reads the current extensions.json file, or returns an empty structure
37
+ */
38
+ function readExtensionsJson(filePath: string): VSCodeExtensionsJson {
39
+ if (!existsSync(filePath)) {
40
+ return { recommendations: [] }
41
+ }
42
+
43
+ try {
44
+ const content = readFileSync(filePath, "utf-8")
45
+ const parsed = JSON.parse(content)
46
+
47
+ if (!Array.isArray(parsed.recommendations)) {
48
+ parsed.recommendations = []
49
+ }
50
+
51
+ return parsed
52
+ } catch (error) {
53
+ console.warn(`Warning: Could not parse ${filePath}, creating new file`)
54
+
55
+ return { recommendations: [] }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Writes the extensions.json file with proper formatting
61
+ */
62
+ function writeExtensionsJson(filePath: string, data: VSCodeExtensionsJson): void {
63
+ const content = JSON.stringify(data, null, 2) + "\n"
64
+
65
+ writeFileSync(filePath, content, "utf-8")
66
+ }
67
+
68
+ /**
69
+ * Adds the Herb VSCode extension to the recommended extensions list
70
+ * Returns true if the extension was added, false if it was already present
71
+ */
72
+ export function addHerbExtensionRecommendation(projectPath: string): boolean {
73
+ const extensionsPath = getExtensionsJsonPath(projectPath)
74
+ const extensions = readExtensionsJson(extensionsPath)
75
+
76
+ if (extensions.recommendations?.includes(HERB_EXTENSION_ID)) {
77
+ return false
78
+ }
79
+
80
+ if (!extensions.recommendations) {
81
+ extensions.recommendations = []
82
+ }
83
+
84
+ extensions.recommendations.push(HERB_EXTENSION_ID)
85
+
86
+ writeExtensionsJson(extensionsPath, extensions)
87
+
88
+ return true
89
+ }
90
+
91
+ /**
92
+ * Gets the relative path to the extensions.json file from the project root
93
+ */
94
+ export function getExtensionsJsonRelativePath(): string {
95
+ return join(VSCODE_DIR, EXTENSIONS_FILE)
96
+ }
package/src/yaml.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ declare module "*.yml" {
2
+ const content: string
3
+ export default content
4
+ }
5
+
6
+ declare module "*.yaml" {
7
+ const content: string
8
+ export default content
9
+ }