Skip to content

Commit c4e54b5

Browse files
committed
refactor(build): enhance build configuration
- Configure esbuild for external dependencies and node: protocol - Add TypeScript source map generation for accurate coverage - Update vitest configuration for better isolation - Update isolated-tests.json
1 parent 2e31dfe commit c4e54b5

File tree

6 files changed

+75
-106
lines changed

6 files changed

+75
-106
lines changed

.config/esbuild.config.mjs

Lines changed: 46 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,25 @@
33
*/
44

55
import { promises as fs } from 'node:fs'
6+
import Module from 'node:module'
67
import path from 'node:path'
78
import { fileURLToPath } from 'node:url'
89

910
import { parse } from '@babel/parser'
1011
import MagicString from 'magic-string'
1112

12-
import { getLocalPackageAliases } from '../scripts/utils/get-local-package-aliases.mjs'
13+
import { NODE_MODULES } from '@socketsecurity/lib/paths/dirnames'
14+
import { envAsBoolean } from '@socketsecurity/lib/env/helpers'
15+
import { getDefaultLogger } from '@socketsecurity/lib/logger'
1316

1417
const __dirname = path.dirname(fileURLToPath(import.meta.url))
18+
1519
const rootPath = path.join(__dirname, '..')
1620
const srcPath = path.join(rootPath, 'src')
1721
const distPath = path.join(rootPath, 'dist')
1822

23+
const logger = getDefaultLogger()
24+
1925
/**
2026
* Plugin to shorten module paths in bundled output with conflict detection.
2127
* Uses @babel/parser and magic-string for precise AST-based modifications.
@@ -31,14 +37,16 @@ function createPathShorteningPlugin() {
3137
)
3238

3339
for (const outputPath of outputs) {
40+
// eslint-disable-next-line no-await-in-loop
3441
const content = await fs.readFile(outputPath, 'utf8')
3542
const magicString = new MagicString(content)
3643

3744
// Track module paths and their shortened versions
3845
const pathMap = new Map()
3946
const conflictDetector = new Map()
4047

41-
function shortenPath(longPath) {
48+
// eslint-disable-next-line unicorn/consistent-function-scoping
49+
const shortenPath = longPath => {
4250
if (pathMap.has(longPath)) {
4351
return pathMap.get(longPath)
4452
}
@@ -67,8 +75,8 @@ function createPathShorteningPlugin() {
6775
if (conflictDetector.has(shortPath)) {
6876
const existingPath = conflictDetector.get(shortPath)
6977
if (existingPath !== longPath) {
70-
console.warn(
71-
`Path conflict detected:\n "${shortPath}"\n Maps to: "${existingPath}"\n Also from: "${longPath}"\n Keeping original paths to avoid conflict.`,
78+
logger.warn(
79+
`Path conflict detected:\n "${shortPath}"\n Maps to: "${existingPath}"\n Also from: "${longPath}"\n Keeping original paths to avoid conflict.`,
7280
)
7381
shortPath = longPath
7482
}
@@ -91,7 +99,7 @@ function createPathShorteningPlugin() {
9199
for (const comment of ast.comments || []) {
92100
if (
93101
comment.type === 'CommentLine' &&
94-
comment.value.includes('node_modules')
102+
comment.value.includes(NODE_MODULES)
95103
) {
96104
const originalPath = comment.value.trim()
97105
const shortPath = shortenPath(originalPath)
@@ -115,7 +123,7 @@ function createPathShorteningPlugin() {
115123
if (
116124
node.type === 'StringLiteral' &&
117125
node.value &&
118-
node.value.includes('node_modules')
126+
node.value.includes(NODE_MODULES)
119127
) {
120128
const originalPath = node.value
121129
const shortPath = shortenPath(originalPath)
@@ -145,11 +153,11 @@ function createPathShorteningPlugin() {
145153
}
146154

147155
walk(ast.program)
156+
// eslint-disable-next-line no-await-in-loop
148157
await fs.writeFile(outputPath, magicString.toString(), 'utf8')
149158
} catch (error) {
150-
console.error(
151-
`Failed to shorten paths in ${outputPath}:`,
152-
error.message,
159+
logger.error(
160+
`Failed to shorten paths in ${outputPath}: ${error.message}`,
153161
)
154162
}
155163
}
@@ -160,57 +168,38 @@ function createPathShorteningPlugin() {
160168
}
161169

162170
/**
163-
* Plugin to handle local package aliases.
164-
* Provides consistent alias resolution across all Socket repos.
171+
* Plugin to ensure all Node.js builtins use the node: protocol.
172+
* Intercepts imports of Node.js built-in modules and rewrites them to use the node: prefix.
165173
*/
166-
function createAliasPlugin() {
167-
const aliases = getLocalPackageAliases(rootPath)
168-
169-
// Only create plugin if we have local aliases
170-
if (Object.keys(aliases).length === 0) {
171-
return null
172-
}
173-
174-
// Packages that should always be bundled (even when using local aliases)
175-
const ALWAYS_BUNDLED = new Set(['@socketsecurity/lib'])
176-
174+
function createNodeProtocolPlugin() {
175+
// Get list of Node.js built-in modules dynamically
177176
return {
178-
name: 'local-package-aliases',
177+
name: 'node-protocol',
179178
setup(build) {
180-
// Intercept imports for aliased packages and mark as external.
181-
for (const [packageName, _aliasPath] of Object.entries(aliases)) {
182-
// Skip packages that should always be bundled - let esbuild bundle them naturally
183-
if (ALWAYS_BUNDLED.has(packageName)) {
179+
for (const builtin of Module.builtinModules) {
180+
// Skip builtins that already have node: prefix
181+
if (builtin.startsWith('node:')) {
184182
continue
185183
}
186184

187-
// Match both exact package name and subpath imports.
185+
// Match imports that don't already have the node: prefix
186+
// Escape special regex characters in module name
187+
const escapedBuiltin = builtin.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
188188
build.onResolve(
189-
{ filter: new RegExp(`^${packageName}(/|$)`) },
190-
args => {
191-
// Mark as external using the original package name to avoid absolute paths in output.
192-
// This ensures require('@socketsecurity/lib') instead of require('/absolute/path/to/socket-lib/dist').
193-
return { path: args.path, external: true }
189+
{ filter: new RegExp(`^${escapedBuiltin}$`) },
190+
_args => {
191+
// Return with node: prefix and mark as external
192+
return {
193+
path: `node:${builtin}`,
194+
external: true,
195+
}
194196
},
195197
)
196198
}
197199
},
198200
}
199201
}
200202

201-
// Get local package aliases for bundled packages
202-
function getBundledPackageAliases() {
203-
const aliases = getLocalPackageAliases(rootPath)
204-
const bundledAliases = {}
205-
206-
// @socketsecurity/lib should always be bundled (not external)
207-
if (aliases['@socketsecurity/lib']) {
208-
bundledAliases['@socketsecurity/lib'] = aliases['@socketsecurity/lib']
209-
}
210-
211-
return bundledAliases
212-
}
213-
214203
// Build configuration for ESM output
215204
export const buildConfig = {
216205
entryPoints: [`${srcPath}/index.ts`, `${srcPath}/testing.ts`],
@@ -222,28 +211,22 @@ export const buildConfig = {
222211
platform: 'node',
223212
// Target Node.js 18+ features.
224213
target: 'node18',
225-
sourcemap: false,
214+
// Enable source maps for coverage (set COVERAGE=true env var)
215+
sourcemap: envAsBoolean(process.env.COVERAGE),
226216
minify: false,
227217
treeShaking: true,
228218
// For bundle analysis
229219
metafile: true,
230220
logLevel: 'info',
231221

232-
// Alias local packages that should be bundled (not external)
233-
alias: getBundledPackageAliases(),
234-
235-
// Use plugin for local package aliases (consistent across all Socket repos).
236-
plugins: [createPathShorteningPlugin(), createAliasPlugin()].filter(Boolean),
222+
// Use plugins for module resolution and path handling.
223+
plugins: [createNodeProtocolPlugin(), createPathShorteningPlugin()].filter(
224+
Boolean,
225+
),
237226

238227
// External dependencies.
239-
// Note: @socketsecurity/lib is bundled (not external) to reduce consumer dependencies.
240-
// With format: 'cjs', bundling CJS code works fine (no __require wrapper issues).
241-
external: [],
242-
243-
// Banner for generated code
244-
banner: {
245-
js: '/* Socket SDK CJS - Built with esbuild */',
246-
},
228+
// @socketsecurity/lib is external (not bundled) - consumers must install it.
229+
external: ['@socketsecurity/lib'],
247230

248231
// TypeScript configuration
249232
tsconfig: path.join(rootPath, 'tsconfig.json'),
@@ -265,12 +248,12 @@ export const watchConfig = {
265248
watch: {
266249
onRebuild(error, result) {
267250
if (error) {
268-
console.error('Watch build failed:', error)
251+
logger.error(`Watch build failed: ${error}`)
269252
} else {
270-
console.log('Watch build succeeded')
253+
logger.log('Watch build succeeded')
271254
if (result.metafile) {
272255
const analysis = analyzeMetafile(result.metafile)
273-
console.log(analysis)
256+
logger.log(analysis)
274257
}
275258
}
276259
},

.config/eslint.config.mjs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import unicornPlugin from 'eslint-plugin-unicorn'
1616
import globals from 'globals'
1717
import tsEslint from 'typescript-eslint'
1818

19-
import { getLocalPackageAliases } from '../scripts/utils/get-local-package-aliases.mjs'
20-
2119
const __filename = fileURLToPath(import.meta.url)
2220
const __dirname = path.dirname(__filename)
2321
const require = createRequire(import.meta.url)
@@ -27,13 +25,7 @@ const getMaintainedNodeVersions = () => ['18', '20', '22', '24']
2725

2826
const rootPath = path.dirname(__dirname)
2927

30-
// Use local config if local Socket packages are detected
31-
const localPackageAliases = getLocalPackageAliases(rootPath)
32-
const hasLocalPackages = Object.keys(localPackageAliases).length > 0
33-
const rootTsConfigPath = path.join(
34-
__dirname,
35-
hasLocalPackages ? 'tsconfig.check.local.json' : 'tsconfig.check.json',
36-
)
28+
const rootTsConfigPath = path.join(__dirname, 'tsconfig.check.json')
3729

3830
const nodeGlobalsConfig = Object.fromEntries(
3931
Object.entries(globals.node).map(([k]) => [k, 'readonly']),
@@ -248,6 +240,7 @@ export default [
248240
'.config/*.config.isolated.mts',
249241
'*.config.mts',
250242
'test/*.mts',
243+
'test/unit/*.mts',
251244
'test/utils/*.mts',
252245
'src/*.mts',
253246
],

.config/isolated-tests.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{
22
"tests": [
3-
"test/quota-utils-error-handling.test.mts",
4-
"test/json-parsing-edge-cases.test.mts",
5-
"test/getapi-sendapi-methods.test.mts",
6-
"test/socket-sdk-retry.test.mts",
7-
"test/entitlements.test.mts",
8-
"test/socket-sdk-batch.test.mts"
3+
"test/unit/quota-utils-error-handling.test.mts",
4+
"test/unit/json-parsing-edge-cases.test.mts",
5+
"test/unit/getapi-sendapi-methods.test.mts",
6+
"test/unit/socket-sdk-retry.test.mts",
7+
"test/unit/entitlements.test.mts",
8+
"test/unit/socket-sdk-batch.test.mts",
9+
"test/unit/socket-sdk-json-parsing-errors.test.mts",
10+
"test/unit/socket-sdk-strict-types.test.mts"
911
]
1012
}

.config/vitest.config.isolated.mts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { fileURLToPath } from 'node:url'
88

99
import { defineConfig } from 'vitest/config'
1010

11-
import { getLocalPackageAliases } from '../scripts/utils/get-local-package-aliases.mjs'
1211
import {
1312
baseCoverageConfig,
1413
isolatedCoverageThresholds,
@@ -29,9 +28,6 @@ const isCoverageEnabled =
2928

3029
export default defineConfig({
3130
cacheDir: './.cache/vitest',
32-
resolve: {
33-
alias: getLocalPackageAliases(path.join(__dirname, '..')),
34-
},
3531
test: {
3632
globals: false,
3733
environment: 'node',
@@ -54,6 +50,7 @@ export default defineConfig({
5450
// Share coverage settings with main config
5551
coverage: {
5652
...baseCoverageConfig,
53+
reportsDirectory: './coverage-isolated',
5754
thresholds: isolatedCoverageThresholds,
5855
},
5956
},

.config/vitest.config.mts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,13 @@
22
* @fileoverview Vitest configuration for Socket SDK test suite.
33
* Configures test environment, coverage, and module resolution.
44
*/
5-
import path from 'node:path'
6-
import { fileURLToPath } from 'node:url'
7-
85
import { defineConfig } from 'vitest/config'
96

10-
import { getLocalPackageAliases } from '../scripts/utils/get-local-package-aliases.mjs'
117
import {
128
baseCoverageConfig,
139
mainCoverageThresholds,
1410
} from './vitest.coverage.config.mts'
1511

16-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
17-
1812
// Check if coverage is enabled via CLI flags or environment.
1913
const isCoverageEnabled =
2014
process.env.COVERAGE === 'true' ||
@@ -28,9 +22,6 @@ if (isCoverageEnabled) {
2822

2923
export default defineConfig({
3024
cacheDir: './.cache/vitest',
31-
resolve: {
32-
alias: getLocalPackageAliases(path.join(__dirname, '..')),
33-
},
3425
test: {
3526
globals: false,
3627
environment: 'node',
@@ -39,12 +30,12 @@ export default defineConfig({
3930
exclude: [
4031
'**/node_modules/**',
4132
'**/dist/**',
42-
'test/quota-utils-error-handling.test.mts',
43-
'test/json-parsing-edge-cases.test.mts',
44-
'test/getapi-sendapi-methods.test.mts',
45-
'test/socket-sdk-retry.test.mts',
46-
'test/entitlements.test.mts',
47-
'test/socket-sdk-batch.test.mts',
33+
'test/unit/quota-utils-error-handling.test.mts',
34+
'test/unit/json-parsing-edge-cases.test.mts',
35+
'test/unit/getapi-sendapi-methods.test.mts',
36+
'test/unit/socket-sdk-retry.test.mts',
37+
'test/unit/entitlements.test.mts',
38+
'test/unit/socket-sdk-batch.test.mts',
4839
],
4940
reporters:
5041
process.env.TEST_REPORTER === 'json' ? ['json', 'default'] : ['default'],
@@ -93,8 +84,9 @@ export default defineConfig({
9384
},
9485
},
9586
// Reduce timeouts for faster failures
96-
testTimeout: 10_000,
97-
hookTimeout: 10_000,
87+
// Increase timeout in coverage mode due to instrumentation overhead
88+
testTimeout: process.env.COVERAGE ? 30_000 : 10_000,
89+
hookTimeout: process.env.COVERAGE ? 30_000 : 10_000,
9890
// Speed optimizations
9991
// Note: cache is now configured via Vite's cacheDir
10092
sequence: {

0 commit comments

Comments
 (0)