dsh-code-index 0.2.0 → 0.3.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.
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * fix-wsl-links — repair pnpm symlinks broken by WSL→Windows installs.
4
+ *
5
+ * Running `pnpm install` from WSL against a checkout on /mnt/c lays down
6
+ * Linux-style symlinks that Windows cannot traverse (EACCES / "cannot find
7
+ * package"). This script walks node_modules, finds every such dead link,
8
+ * and re-points it at its real target inside .pnpm as a Windows junction.
9
+ *
10
+ * Run from Windows-side Node (the dsh-bundled node.exe works):
11
+ *
12
+ * node.exe scripts\fix-wsl-links.mjs [root]
13
+ *
14
+ * `root` defaults to the repo this script lives in. Pass a dsh profile
15
+ * directory (…\.dsh\profiles\web) to fix a plugin install instead.
16
+ */
17
+
18
+ import { existsSync, readdirSync, readlinkSync, realpathSync, rmSync, symlinkSync, lstatSync } from 'node:fs'
19
+ import path from 'node:path'
20
+ import { fileURLToPath } from 'node:url'
21
+
22
+ const defaultRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
23
+ const root = path.resolve(process.argv[2] ?? defaultRoot)
24
+ const nm = path.join(root, 'node_modules')
25
+ const pnpm = path.join(nm, '.pnpm')
26
+
27
+ if (!existsSync(nm)) {
28
+ console.error(`no node_modules under ${root} — run pnpm install first`)
29
+ process.exit(1)
30
+ }
31
+ if (process.platform === 'win32' && !existsSync(pnpm)) {
32
+ console.error(`no .pnpm store under ${nm} — nothing to re-link against`)
33
+ process.exit(1)
34
+ }
35
+
36
+ let fixed = 0
37
+ let checked = 0
38
+
39
+ /** True for a link entry whose target Windows Node cannot resolve. */
40
+ function broken(link) {
41
+ try {
42
+ realpathSync(link)
43
+ return false
44
+ } catch {
45
+ return true
46
+ }
47
+ }
48
+
49
+ /** Replace `link` with a junction to `target`; report and count. */
50
+ function junction(link, target) {
51
+ rmSync(link, { force: true, recursive: true })
52
+ try {
53
+ symlinkSync(target, link, 'junction')
54
+ } catch {
55
+ try {
56
+ rmSync(link, { force: true, recursive: true })
57
+ } catch {}
58
+ console.error(` FAIL ${rel(link)} → ${target}`)
59
+ return
60
+ }
61
+ fixed++
62
+ console.log(` fixed ${rel(link)}`)
63
+ }
64
+
65
+ function rel(p) {
66
+ return path.relative(root, p) || p
67
+ }
68
+
69
+ /** Resolve the canonical store dir for a package: .pnpm/<+name>@<ver>/node_modules</name>. */
70
+ function storeDir(pkgName, versionHint) {
71
+ const storeName = pkgName.replace('/', '+')
72
+ if (versionHint) {
73
+ const direct = path.join(pnpm, `${storeName}@${versionHint}`)
74
+ if (existsSync(direct)) return direct
75
+ }
76
+ const prefix = `${storeName}@`
77
+ const hit = readdirSync(pnpm).find(
78
+ (d) => d.startsWith(prefix) && existsSync(path.join(pnpm, d, 'node_modules', pkgName)),
79
+ )
80
+ return hit ? path.join(pnpm, hit) : null
81
+ }
82
+
83
+ /** Fix every dead scoped/unscoped link directly under one node_modules. */
84
+ function fixLevel(levelNm, scope = null) {
85
+ for (const entry of readdirSync(levelNm)) {
86
+ const link = path.join(levelNm, entry)
87
+ let st
88
+ try {
89
+ st = lstatSync(link)
90
+ } catch {
91
+ continue
92
+ }
93
+ if (!st.isSymbolicLink()) {
94
+ if (st.isDirectory() && entry.startsWith('@')) fixLevel(link, entry)
95
+ continue
96
+ }
97
+ checked++
98
+ if (!broken(link)) continue
99
+
100
+ const target = readlinkSync(link)
101
+ const pkgName = scope ? `${scope}/${entry}` : entry
102
+ const storeName = pkgName.replace('/', '+')
103
+
104
+ // pnpm layout: <link> → .pnpm/<pkg>@<ver[_peer]>…/node_modules/<pkg>.
105
+ // Re-derive that dir; the old target string is Linux-shaped and useless.
106
+ const base = target.split('node_modules/')[0]?.split('/').pop() ?? ''
107
+ const versionHint = base.startsWith(`${storeName}@`) ? base : null
108
+ const store = storeDir(pkgName, versionHint)
109
+ let resolved = store ? path.join(store, 'node_modules', pkgName) : null
110
+
111
+ // Scoped aliases link a nested path (e.g. @standard-schema/spec lives in
112
+ // the @standard-schema+spec store); fall back to matching by the target's
113
+ // node_modules tail — or, when the target names no node_modules at all
114
+ // (dangling junk), by the package name itself.
115
+ if (!resolved) {
116
+ const tail = target.includes('node_modules/')
117
+ ? target.split('node_modules/').pop()
118
+ : pkgName
119
+ for (const dir of readdirSync(pnpm)) {
120
+ const candidate = path.join(pnpm, dir, 'node_modules', tail)
121
+ if (existsSync(candidate)) {
122
+ resolved = candidate
123
+ break
124
+ }
125
+ }
126
+ }
127
+
128
+ if (resolved && existsSync(resolved)) {
129
+ junction(link, resolved)
130
+ continue
131
+ }
132
+
133
+ // Non-store link (workspace `link:` deps, profile installs): re-point at
134
+ // the original Windows path when that still exists.
135
+ const asWin = target.replaceAll('/', '\\')
136
+ if (existsSync(asWin)) {
137
+ junction(link, asWin)
138
+ continue
139
+ }
140
+ console.error(` no target for ${rel(link)} (${target}) — remove or reinstall`)
141
+ }
142
+ }
143
+
144
+ if (existsSync(pnpm)) {
145
+ for (const dir of readdirSync(pnpm)) {
146
+ const levelNm = path.join(pnpm, dir, 'node_modules')
147
+ if (existsSync(levelNm)) fixLevel(levelNm)
148
+ }
149
+ }
150
+ fixLevel(nm)
151
+
152
+ console.log(`\n${root}`)
153
+ console.log(`links checked: ${checked}, fixed: ${fixed}`)
154
+ process.exit(fixed > 0 ? 0 : 0)