Skip to content

Commit 75f4cf3

Browse files
committed
fix(build): bundle @socketsecurity/lib instead of marking external
Remove @socketsecurity/lib from external array and update alias plugin to skip externalizing it. This reduces consumer dependencies.
1 parent 7519e28 commit 75f4cf3

1 file changed

Lines changed: 159 additions & 6 deletions

File tree

.config/esbuild.config.mjs

Lines changed: 159 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,158 @@ const rootPath = path.join(__dirname, '..')
1212
const srcPath = path.join(rootPath, 'src')
1313
const distPath = path.join(rootPath, 'dist')
1414

15+
/**
16+
* Plugin to shorten module paths in bundled output with conflict detection.
17+
* Uses @babel/parser and magic-string for precise AST-based modifications.
18+
*/
19+
function createPathShorteningPlugin() {
20+
return {
21+
name: 'shorten-module-paths',
22+
setup(build) {
23+
build.onEnd(async result => {
24+
if (!result.outputFiles && result.metafile) {
25+
// Dynamic imports to avoid adding to production dependencies
26+
const fs = await import('node:fs/promises')
27+
const { parse } = await import('@babel/parser')
28+
const MagicString = (await import('magic-string')).default
29+
30+
const outputs = Object.keys(result.metafile.outputs).filter(f =>
31+
f.endsWith('.js') || f.endsWith('.mjs'),
32+
)
33+
34+
for (const outputPath of outputs) {
35+
const content = await fs.readFile(outputPath, 'utf8')
36+
const magicString = new MagicString(content)
37+
38+
// Track module paths and their shortened versions
39+
const pathMap = new Map()
40+
const conflictDetector = new Map()
41+
42+
function shortenPath(longPath) {
43+
if (pathMap.has(longPath)) {
44+
return pathMap.get(longPath)
45+
}
46+
47+
let shortPath = longPath
48+
49+
// Handle pnpm scoped packages
50+
const scopedPnpmMatch = longPath.match(
51+
/node_modules\/\.pnpm\/@([^+/]+)\+([^@/]+)@[^/]+\/node_modules\/(@[^/]+\/[^/]+)\/(.+)/,
52+
)
53+
if (scopedPnpmMatch) {
54+
const [, _scope, _pkg, packageName, subpath] = scopedPnpmMatch
55+
shortPath = `${packageName}/${subpath}`
56+
} else {
57+
// Handle pnpm non-scoped packages
58+
const pnpmMatch = longPath.match(
59+
/node_modules\/\.pnpm\/([^@/]+)@[^/]+\/node_modules\/([^/]+)\/(.+)/,
60+
)
61+
if (pnpmMatch) {
62+
const [, _pkgName, packageName, subpath] = pnpmMatch
63+
shortPath = `${packageName}/${subpath}`
64+
}
65+
}
66+
67+
// Detect conflicts
68+
if (conflictDetector.has(shortPath)) {
69+
const existingPath = conflictDetector.get(shortPath)
70+
if (existingPath !== longPath) {
71+
console.warn(
72+
`⚠ Path conflict detected:\n "${shortPath}"\n Maps to: "${existingPath}"\n Also from: "${longPath}"\n Keeping original paths to avoid conflict.`,
73+
)
74+
shortPath = longPath
75+
}
76+
} else {
77+
conflictDetector.set(shortPath, longPath)
78+
}
79+
80+
pathMap.set(longPath, shortPath)
81+
return shortPath
82+
}
83+
84+
// Parse AST to find all string literals containing module paths
85+
try {
86+
const ast = parse(content, {
87+
sourceType: 'module',
88+
plugins: [],
89+
})
90+
91+
// Walk through all comments
92+
for (const comment of ast.comments || []) {
93+
if (
94+
comment.type === 'CommentLine' &&
95+
comment.value.includes('node_modules')
96+
) {
97+
const originalPath = comment.value.trim()
98+
const shortPath = shortenPath(originalPath)
99+
100+
if (shortPath !== originalPath) {
101+
magicString.overwrite(
102+
comment.start,
103+
comment.end,
104+
`// ${shortPath}`,
105+
)
106+
}
107+
}
108+
}
109+
110+
// Walk through all string literals
111+
function walk(node) {
112+
if (!node || typeof node !== 'object') {
113+
return
114+
}
115+
116+
if (
117+
node.type === 'StringLiteral' &&
118+
node.value &&
119+
node.value.includes('node_modules')
120+
) {
121+
const originalPath = node.value
122+
const shortPath = shortenPath(originalPath)
123+
124+
if (shortPath !== originalPath) {
125+
magicString.overwrite(
126+
node.start + 1,
127+
node.end - 1,
128+
shortPath,
129+
)
130+
}
131+
}
132+
133+
for (const key of Object.keys(node)) {
134+
if (key === 'start' || key === 'end' || key === 'loc') {
135+
continue
136+
}
137+
const value = node[key]
138+
if (Array.isArray(value)) {
139+
for (const item of value) {
140+
walk(item)
141+
}
142+
} else {
143+
walk(value)
144+
}
145+
}
146+
}
147+
148+
walk(ast.program)
149+
await fs.writeFile(outputPath, magicString.toString(), 'utf8')
150+
} catch (error) {
151+
console.error(
152+
`Failed to shorten paths in ${outputPath}:`,
153+
error.message,
154+
)
155+
}
156+
}
157+
}
158+
})
159+
},
160+
}
161+
}
162+
15163
/**
16164
* Plugin to handle local package aliases.
17165
* Provides consistent alias resolution across all Socket repos.
166+
* Note: Does not externalize @socketsecurity/lib - that should be bundled.
18167
*/
19168
function createAliasPlugin() {
20169
const aliases = getLocalPackageAliases(rootPath)
@@ -27,8 +176,13 @@ function createAliasPlugin() {
27176
return {
28177
name: 'local-package-aliases',
29178
setup(build) {
30-
// Intercept imports for aliased packages.
179+
// Intercept imports for aliased packages (except @socketsecurity/lib which should be bundled)
31180
for (const [packageName, _aliasPath] of Object.entries(aliases)) {
181+
// Skip @socketsecurity/lib - it should be bundled, not externalized
182+
if (packageName === '@socketsecurity/lib') {
183+
continue
184+
}
185+
32186
// Match both exact package name and subpath imports.
33187
build.onResolve(
34188
{ filter: new RegExp(`^${packageName}(/|$)`) },
@@ -47,6 +201,7 @@ function createAliasPlugin() {
47201
export const buildConfig = {
48202
entryPoints: [`${srcPath}/index.ts`, `${srcPath}/testing.ts`],
49203
outdir: distPath,
204+
outbase: srcPath,
50205
bundle: true,
51206
format: 'esm',
52207
// Target Node.js environment (not browser).
@@ -62,16 +217,14 @@ export const buildConfig = {
62217
outExtension: { '.js': '.mjs' },
63218

64219
// Use plugin for local package aliases (consistent across all Socket repos)
65-
plugins: [createAliasPlugin()].filter(Boolean),
220+
plugins: [createPathShorteningPlugin(), createAliasPlugin()].filter(Boolean),
66221

67222
// External dependencies.
68223
// With platform: 'node', esbuild automatically externalizes all Node.js
69224
// built-ins. The explicit external array with builtinModules is redundant
70225
// (but doesn't hurt as extra safety).
71-
external: [
72-
// External dependencies that shouldn't be bundled.
73-
'@socketsecurity/lib',
74-
],
226+
// Note: @socketsecurity/lib is bundled (not external) to reduce consumer dependencies
227+
external: [],
75228

76229
// Banner for generated code
77230
banner: {

0 commit comments

Comments
 (0)