Skip to content

Commit 98fb9d5

Browse files
committed
fix(build): prevent absolute paths in esbuild output and add bundle validation
- Update alias plugin to use original package name (args.path) instead of resolved absolute path when marking as external - Add external dependencies array (@socketsecurity/lib, @socketsecurity/registry, @socketregistry/packageurl-js) to esbuild config - Add bundle validation test to detect absolute paths and external dependency issues in dist files This fixes the issue where require("/Users/jdalton/projects/socket-lib/dist") appeared in dist/index.js, which prevented socket-cli builds from completing.
1 parent 6d4091d commit 98fb9d5

File tree

2 files changed

+122
-7
lines changed

2 files changed

+122
-7
lines changed

.config/esbuild.config.mjs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,14 @@ function createAliasPlugin() {
2929
name: 'local-package-aliases',
3030
setup(build) {
3131
// Intercept imports for aliased packages
32-
for (const [packageName, aliasPath] of Object.entries(aliases)) {
32+
for (const [packageName, _aliasPath] of Object.entries(aliases)) {
3333
// Match both exact package name and subpath imports
3434
build.onResolve(
3535
{ filter: new RegExp(`^${packageName}(/|$)`) },
3636
args => {
37-
// Handle subpath imports like '@socketsecurity/lib/spinner'
38-
const subpath = args.path.slice(packageName.length + 1)
39-
const resolvedPath = subpath
40-
? path.join(aliasPath, subpath)
41-
: aliasPath
42-
return { path: resolvedPath, external: true }
37+
// Mark as external using the original package name to avoid absolute paths in output.
38+
// This ensures require('@socketsecurity/lib') instead of require('/absolute/path/to/socket-lib/dist').
39+
return { path: args.path, external: true }
4340
},
4441
)
4542
}
@@ -74,6 +71,10 @@ export const buildConfig = {
7471
// Node.js built-ins
7572
...builtinModules,
7673
...builtinModules.map(m => `node:${m}`),
74+
// Package dependencies (should be resolved by consumers)
75+
'@socketsecurity/lib',
76+
'@socketsecurity/registry',
77+
'@socketregistry/packageurl-js',
7778
],
7879

7980
// Banner for generated code

test/bundle-validation.test.mts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* @fileoverview Bundle validation tests to ensure build output quality.
3+
* Verifies that dist files don't contain absolute paths or external dependencies.
4+
*/
5+
6+
import { promises as fs } from 'node:fs'
7+
import path from 'node:path'
8+
import { fileURLToPath } from 'node:url'
9+
10+
import { describe, expect, it } from 'vitest'
11+
12+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
13+
const packagePath = path.resolve(__dirname, '..')
14+
const distPath = path.join(packagePath, 'dist')
15+
16+
/**
17+
* Check if content contains absolute paths.
18+
* Detects paths like /Users/, C:\, /home/, etc.
19+
*/
20+
function hasAbsolutePaths(content: string): {
21+
hasIssue: boolean
22+
matches: string[]
23+
} {
24+
// Match absolute paths but exclude URLs and node: protocol.
25+
const patterns = [
26+
// Match require('/abs/path') or require('C:\\path').
27+
/require\(["'](?:\/[^"'\n]+|[A-Z]:\\[^"'\n]+)["']\)/g,
28+
// Match import from '/abs/path'.
29+
/import\s+.*?from\s+["'](?:\/[^"'\n]+|[A-Z]:\\[^"'\n]+)["']/g,
30+
]
31+
32+
const matches: string[] = []
33+
for (const pattern of patterns) {
34+
const found = content.match(pattern)
35+
if (found) {
36+
matches.push(...found)
37+
}
38+
}
39+
40+
return {
41+
hasIssue: matches.length > 0,
42+
matches,
43+
}
44+
}
45+
46+
/**
47+
* Check if content is missing external dependencies (they should be require() calls).
48+
* External dependencies should NOT be bundled inline.
49+
*/
50+
function checkExternalDependencies(content: string): {
51+
missingRequires: string[]
52+
hasAllRequires: boolean
53+
} {
54+
// Dependencies that should be external (as require() calls).
55+
const externalDeps = ['@socketsecurity/lib']
56+
57+
const missingRequires: string[] = []
58+
59+
for (const dep of externalDeps) {
60+
// Check if the bundle has require() calls for this dependency.
61+
const requirePattern = new RegExp(
62+
`require\\(["']${dep.replace('/', '\\/')}["']\\)`,
63+
)
64+
const hasRequire = requirePattern.test(content)
65+
66+
if (!hasRequire) {
67+
missingRequires.push(dep)
68+
}
69+
}
70+
71+
return {
72+
missingRequires,
73+
hasAllRequires: missingRequires.length === 0,
74+
}
75+
}
76+
77+
describe('Bundle validation', () => {
78+
it('should not contain absolute paths in dist/index.js', async () => {
79+
const indexPath = path.join(distPath, 'index.js')
80+
const content = await fs.readFile(indexPath, 'utf8')
81+
82+
const result = hasAbsolutePaths(content)
83+
84+
if (result.hasIssue) {
85+
console.error('Found absolute paths in bundle:')
86+
for (const match of result.matches) {
87+
console.error(` - ${match}`)
88+
}
89+
}
90+
91+
expect(result.hasIssue, 'Bundle should not contain absolute paths').toBe(
92+
false,
93+
)
94+
})
95+
96+
it('should have external dependencies as require() calls', async () => {
97+
const indexPath = path.join(distPath, 'index.js')
98+
const content = await fs.readFile(indexPath, 'utf8')
99+
100+
const result = checkExternalDependencies(content)
101+
102+
if (!result.hasAllRequires) {
103+
console.error('Missing require() calls for external dependencies:')
104+
for (const dep of result.missingRequires) {
105+
console.error(` - ${dep}`)
106+
}
107+
}
108+
109+
expect(
110+
result.hasAllRequires,
111+
'All external dependencies should be require() calls, not bundled inline',
112+
).toBe(true)
113+
})
114+
})

0 commit comments

Comments
 (0)