@@ -13,9 +13,178 @@ const rootPath = path.join(__dirname, '..')
1313const srcPath = path . join ( rootPath , 'src' )
1414const distPath = path . join ( rootPath , 'dist' )
1515
16+ /**
17+ * Plugin to shorten module paths in bundled output with conflict detection.
18+ * Uses @babel/parser and magic-string for precise AST-based modifications.
19+ */
20+ function createPathShorteningPlugin ( ) {
21+ return {
22+ name : 'shorten-module-paths' ,
23+ setup ( build ) {
24+ build . onEnd ( async result => {
25+ if ( ! result . outputFiles && result . metafile ) {
26+ // Dynamic imports to avoid adding to production dependencies
27+ const fs = await import ( 'node:fs/promises' )
28+ const { parse } = await import ( '@babel/parser' )
29+ const MagicString = ( await import ( 'magic-string' ) ) . default
30+
31+ const outputs = Object . keys ( result . metafile . outputs ) . filter ( f =>
32+ f . endsWith ( '.js' ) ,
33+ )
34+
35+ for ( const outputPath of outputs ) {
36+ const content = await fs . readFile ( outputPath , 'utf8' )
37+ const magicString = new MagicString ( content )
38+
39+ // Track module paths and their shortened versions
40+ // Map<originalPath, shortenedPath>
41+ const pathMap = new Map ( )
42+ // Track shortened paths to detect conflicts
43+ // Map<shortenedPath, originalPath>
44+ const conflictDetector = new Map ( )
45+
46+ /**
47+ * Shorten a module path and detect conflicts.
48+ */
49+ function shortenPath ( longPath ) {
50+ if ( pathMap . has ( longPath ) ) {
51+ return pathMap . get ( longPath )
52+ }
53+
54+ let shortPath = longPath
55+
56+ // Handle pnpm scoped packages
57+ // node_modules/.pnpm/@scope +pkg@version/node_modules/@scope/pkg/dist/file.js
58+ // -> @scope/pkg/dist/file.js
59+ const scopedPnpmMatch = longPath . match (
60+ / n o d e _ m o d u l e s \/ \. p n p m \/ @ ( [ ^ + / ] + ) \+ ( [ ^ @ / ] + ) @ [ ^ / ] + \/ n o d e _ m o d u l e s \/ ( @ [ ^ / ] + \/ [ ^ / ] + ) \/ ( .+ ) / ,
61+ )
62+ if ( scopedPnpmMatch ) {
63+ const [ , _scope , _pkg , packageName , subpath ] = scopedPnpmMatch
64+ shortPath = `${ packageName } /${ subpath } `
65+ } else {
66+ // Handle pnpm non-scoped packages
67+ // node_modules/.pnpm/pkg@version /node_modules/pkg/dist/file.js
68+ // -> pkg/dist/file.js
69+ const pnpmMatch = longPath . match (
70+ / n o d e _ m o d u l e s \/ \. p n p m \/ ( [ ^ @ / ] + ) @ [ ^ / ] + \/ n o d e _ m o d u l e s \/ ( [ ^ / ] + ) \/ ( .+ ) / ,
71+ )
72+ if ( pnpmMatch ) {
73+ const [ , _pkgName , packageName , subpath ] = pnpmMatch
74+ shortPath = `${ packageName } /${ subpath } `
75+ }
76+ }
77+
78+ // Detect conflicts
79+ if ( conflictDetector . has ( shortPath ) ) {
80+ const existingPath = conflictDetector . get ( shortPath )
81+ if ( existingPath !== longPath ) {
82+ // Conflict detected - keep original path
83+ console . warn (
84+ `⚠ Path conflict detected:\n "${ shortPath } "\n Maps to: "${ existingPath } "\n Also from: "${ longPath } "\n Keeping original paths to avoid conflict.` ,
85+ )
86+ shortPath = longPath
87+ }
88+ } else {
89+ conflictDetector . set ( shortPath , longPath )
90+ }
91+
92+ pathMap . set ( longPath , shortPath )
93+ return shortPath
94+ }
95+
96+ // Parse AST to find all string literals containing module paths
97+ try {
98+ const ast = parse ( content , {
99+ sourceType : 'module' ,
100+ plugins : [ ] ,
101+ } )
102+
103+ // Walk through all comments (esbuild puts module paths in comments)
104+ for ( const comment of ast . comments || [ ] ) {
105+ if (
106+ comment . type === 'CommentLine' &&
107+ comment . value . includes ( 'node_modules' )
108+ ) {
109+ const originalPath = comment . value . trim ( )
110+ const shortPath = shortenPath ( originalPath )
111+
112+ if ( shortPath !== originalPath ) {
113+ // Replace in comment
114+ const commentStart = comment . start
115+ const commentEnd = comment . end
116+ magicString . overwrite (
117+ commentStart ,
118+ commentEnd ,
119+ `// ${ shortPath } ` ,
120+ )
121+ }
122+ }
123+ }
124+
125+ // Walk through all string literals in __commonJS calls
126+ function walk ( node ) {
127+ if ( ! node || typeof node !== 'object' ) {
128+ return
129+ }
130+
131+ // Check for string literals containing node_modules paths
132+ if (
133+ node . type === 'StringLiteral' &&
134+ node . value &&
135+ node . value . includes ( 'node_modules' )
136+ ) {
137+ const originalPath = node . value
138+ const shortPath = shortenPath ( originalPath )
139+
140+ if ( shortPath !== originalPath ) {
141+ // Replace the string content (keep quotes)
142+ magicString . overwrite (
143+ node . start + 1 ,
144+ node . end - 1 ,
145+ shortPath ,
146+ )
147+ }
148+ }
149+
150+ // Recursively walk all properties
151+ for ( const key of Object . keys ( node ) ) {
152+ if ( key === 'start' || key === 'end' || key === 'loc' ) {
153+ continue
154+ }
155+ const value = node [ key ]
156+ if ( Array . isArray ( value ) ) {
157+ for ( const item of value ) {
158+ walk ( item )
159+ }
160+ } else {
161+ walk ( value )
162+ }
163+ }
164+ }
165+
166+ walk ( ast . program )
167+
168+ // Write the modified content
169+ await fs . writeFile ( outputPath , magicString . toString ( ) , 'utf8' )
170+ } catch ( error ) {
171+ console . error (
172+ `Failed to shorten paths in ${ outputPath } :` ,
173+ error . message ,
174+ )
175+ // Continue without failing the build
176+ }
177+ }
178+ }
179+ } )
180+ } ,
181+ }
182+ }
183+
16184/**
17185 * Plugin to handle local package aliases.
18186 * Provides consistent alias resolution across all Socket repos.
187+ * Note: Does not externalize @socketsecurity/lib - that should be bundled.
19188 */
20189function createAliasPlugin ( ) {
21190 const aliases = getLocalPackageAliases ( rootPath )
@@ -28,8 +197,13 @@ function createAliasPlugin() {
28197 return {
29198 name : 'local-package-aliases' ,
30199 setup ( build ) {
31- // Intercept imports for aliased packages
200+ // Intercept imports for aliased packages (except @socketsecurity/lib which should be bundled)
32201 for ( const [ packageName , _aliasPath ] of Object . entries ( aliases ) ) {
202+ // Skip @socketsecurity /lib - it should be bundled, not externalized
203+ if ( packageName === '@socketsecurity/lib' ) {
204+ continue
205+ }
206+
33207 // Match both exact package name and subpath imports
34208 build . onResolve (
35209 { filter : new RegExp ( `^${ packageName } (/|$)` ) } ,
@@ -48,6 +222,7 @@ function createAliasPlugin() {
48222export const buildConfig = {
49223 entryPoints : [ `${ srcPath } /index.ts` ] ,
50224 outdir : distPath ,
225+ outbase : srcPath ,
51226 bundle : true ,
52227 format : 'cjs' ,
53228 platform : 'node' ,
@@ -63,18 +238,14 @@ export const buildConfig = {
63238 // Preserve module structure for better tree-shaking
64239 splitting : false ,
65240
66- // Use plugin for local package aliases (consistent across all Socket repos)
67- plugins : [ createAliasPlugin ( ) ] . filter ( Boolean ) ,
241+ // Use plugins for local package aliases and path shortening
242+ plugins : [ createPathShorteningPlugin ( ) , createAliasPlugin ( ) ] . filter ( Boolean ) ,
68243
69244 // External dependencies
70245 external : [
71246 // Node.js built-ins
72247 ...builtinModules ,
73248 ...builtinModules . map ( m => `node:${ m } ` ) ,
74- // Package dependencies (should be resolved by consumers)
75- '@socketsecurity/lib' ,
76- '@socketsecurity/registry' ,
77- '@socketregistry/packageurl-js' ,
78249 ] ,
79250
80251 // Banner for generated code
0 commit comments