Skip to content

Commit 522eed1

Browse files
committed
fix(smol): fix MODULE_NOT_FOUND error for socketsecurity bootstrap
This commit fixes the recurring MODULE_NOT_FOUND error for 'internal/bootstrap/socketsecurity' that was preventing the smol Node.js binary from executing. Changes: 1. **Fixed preload patch** (load-socketsecurity-bootstrap-v24-preexec.patch): - Changed from `_preloadModules(['internal/bootstrap/socketsecurity'])` - To direct `require('internal/bootstrap/socketsecurity')` - Reason: `_preloadModules()` treats paths as filesystem modules, while `require()` properly loads internal builtins through the brotli-aware builtin loader 2. **Removed redundant JavaScript brotli functions** (build.mjs): - Deleted `_compressNodeJsWithBrotli()` and `createBrotliBootstrapHook()` - These were redundant with the C++ brotli system (socket_brotli2c) - The C++ system already handles compression via: * socket_brotli2c tool compresses JS files at build time * socket_brotli_builtin_loader.h decompresses at runtime * No .js.br files needed Technical details: The C++ brotli system works by: - Compressing lib/ files with "BROT" magic header during build - Embedding as C++ byte arrays in node_javascript.cc - Transparently decompressing via LoadBuiltinSourceWithBrotli() - Working with original .js extensions (no filesystem files needed) The JavaScript system was creating duplicate .js.br files on disk and using Module._preloadModules() which couldn't access builtins properly. Resolves the MODULE_NOT_FOUND error in both local and CI builds.
1 parent 4259926 commit 522eed1

File tree

2 files changed

+3
-278
lines changed

2 files changed

+3
-278
lines changed

packages/node-smol-builder/patches/load-socketsecurity-bootstrap-v24-preexec.patch

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,12 @@
66

77
--- a/lib/internal/process/pre_execution.js
88
+++ b/lib/internal/process/pre_execution.js
9-
@@ -698,6 +698,14 @@ function initializeReport() {
9+
@@ -698,6 +698,9 @@ function initializeReport() {
1010
}
1111

1212
function loadPreloadModules() {
13-
+ // Load Socket security bootstrap first (internal resource).
14-
+ const {
15-
+ Module: {
16-
+ _preloadModules,
17-
+ },
18-
+ } = require('internal/modules/cjs/loader');
19-
+ _preloadModules(['internal/bootstrap/socketsecurity']);
13+
+ // Load Socket security bootstrap first (internal builtin).
14+
+ require('internal/bootstrap/socketsecurity');
2015
+
2116
// For user code, we preload modules if `-r` is passed
2217
const preloadModules = getOptionValue('--require');

packages/node-smol-builder/scripts/build.mjs

Lines changed: 0 additions & 270 deletions
Original file line numberDiff line numberDiff line change
@@ -718,276 +718,6 @@ async function verifySocketModifications() {
718718
// Socket modifications must be applied via patches only.
719719
// If patches fail, the build should fail with helpful error messages.
720720

721-
/**
722-
* Minify and compress Node.js JavaScript files for maximum binary size reduction.
723-
*
724-
* This function provides two-stage compression of all JavaScript files in the
725-
* Node.js lib/ directory:
726-
* 1. Minify with esbuild (removes whitespace, shortens identifiers)
727-
* 2. Compress with Brotli at maximum quality
728-
*
729-
* It's safe to use because:
730-
* 1. Original .js files remain intact (only .js.br files are created)
731-
* 2. Bootstrap hook provides transparent decompression at runtime
732-
* 3. Only files with >10% total compression savings are saved
733-
*
734-
* Expected savings: ~2.5 MB additional binary size reduction.
735-
* Compression ratio: Typically 83-85% for minified + Brotli compressed files.
736-
*
737-
* How it works:
738-
* 1. Walk all .js files in NODE_DIR/lib/ recursively
739-
* 2. Minify each with esbuild (removes whitespace, shortens names)
740-
* 3. Compress minified code with Brotli at maximum quality (level 11)
741-
* 4. Save as .js.br if total compression saves >10% space
742-
* 5. Track statistics for reporting
743-
*
744-
* The compressed files are automatically decompressed by the bootstrap hook
745-
* created in createBrotliBootstrapHook() below.
746-
*/
747-
async function _compressNodeJsWithBrotli() {
748-
printHeader('Minifying and Compressing Node.js JavaScript')
749-
750-
const { _stat, readFile, readdir, writeFile } = await import(
751-
'node:fs/promises'
752-
)
753-
const { relative } = await import('node:path')
754-
const { build } = await import('esbuild')
755-
756-
// Track compression statistics for reporting.
757-
let totalOriginalSize = 0
758-
let totalMinifiedSize = 0
759-
let totalCompressedSize = 0
760-
let filesCompressed = 0
761-
762-
// Async generator to recursively walk directory tree and yield .js files.
763-
async function* walkDir(dir) {
764-
const entries = await readdir(dir, { withFileTypes: true })
765-
for (const entry of entries) {
766-
const fullPath = join(dir, entry.name)
767-
if (entry.isDirectory()) {
768-
yield* walkDir(fullPath)
769-
} else if (entry.isFile() && entry.name.endsWith('.js')) {
770-
yield fullPath
771-
}
772-
}
773-
}
774-
775-
logger.log('Minifying and compressing JavaScript files...')
776-
logger.log('')
777-
778-
const libDir = join(NODE_DIR, 'lib')
779-
780-
// Process each JavaScript file in the lib/ directory.
781-
for await (const jsFile of walkDir(libDir)) {
782-
try {
783-
// Read original file contents.
784-
const original = await readFile(jsFile, 'utf8')
785-
const originalSize = Buffer.byteLength(original, 'utf8')
786-
787-
// Step 1: Minify with esbuild.
788-
// This removes whitespace, shortens variable names, and applies syntax optimizations.
789-
const minifyResult = await build({
790-
stdin: {
791-
contents: original,
792-
loader: 'js',
793-
sourcefile: jsFile,
794-
},
795-
write: false,
796-
minify: true,
797-
minifyWhitespace: true,
798-
minifyIdentifiers: true,
799-
minifySyntax: true,
800-
target: 'node22',
801-
format: 'cjs',
802-
platform: 'node',
803-
logLevel: 'silent',
804-
// Preserve Node.js built-in behavior.
805-
keepNames: false, // Safe: Node.js core doesn't rely on Function.name.
806-
treeShaking: false, // Keep all code (Node.js modules are entry points).
807-
})
808-
809-
const minifiedCode = minifyResult.outputFiles[0].text
810-
const minifiedSize = Buffer.byteLength(minifiedCode, 'utf8')
811-
812-
// Step 2: Compress minified code with Brotli at maximum quality.
813-
// BROTLI_PARAM_QUALITY: Level 11 (maximum, best compression ratio).
814-
// BROTLI_MODE_TEXT: Optimized for text data like JavaScript source.
815-
const compressed = brotliCompressSync(minifiedCode, {
816-
params: {
817-
[zlibConstants.BROTLI_PARAM_QUALITY]:
818-
zlibConstants.BROTLI_MAX_QUALITY, // Quality 11.
819-
[zlibConstants.BROTLI_PARAM_MODE]: zlibConstants.BROTLI_MODE_TEXT, // Text mode for JS.
820-
},
821-
})
822-
823-
const compressedSize = compressed.length
824-
const totalRatio = ((1 - compressedSize / originalSize) * 100).toFixed(1)
825-
826-
// Only keep compressed version if we save >10% total space.
827-
// Small files (<10% savings) aren't worth the decompression overhead.
828-
if (compressedSize < originalSize * 0.9) {
829-
// Write .js.br file alongside original .js file.
830-
// The bootstrap hook will prefer .br when available.
831-
await writeFile(`${jsFile}.br`, compressed)
832-
833-
// Update running totals.
834-
totalOriginalSize += originalSize
835-
totalMinifiedSize += minifiedSize
836-
totalCompressedSize += compressedSize
837-
filesCompressed++
838-
839-
// Log individual file processing results.
840-
const relativePath = relative(libDir, jsFile)
841-
logger.log(
842-
` ${colors.green('✓')} ${relativePath.padEnd(50)} ` +
843-
`${(originalSize / 1024).toFixed(1)}KB → ` +
844-
`${(minifiedSize / 1024).toFixed(1)}KB → ` +
845-
`${(compressedSize / 1024).toFixed(1)}KB ` +
846-
`(-${totalRatio}%)`,
847-
)
848-
}
849-
} catch (e) {
850-
// Skip files that can't be minified/compressed (permissions, syntax errors, etc.).
851-
logger.warn(` ${colors.yellow('⚠')} Skipped ${relative(NODE_DIR, jsFile)}: ${e.message}`)
852-
}
853-
}
854-
855-
logger.log('')
856-
logger.log(`${colors.green('✓')} Processed ${filesCompressed} files`)
857-
logger.log(
858-
` Original: ${(totalOriginalSize / 1024 / 1024).toFixed(2)} MB`,
859-
)
860-
logger.log(
861-
` Minified: ${(totalMinifiedSize / 1024 / 1024).toFixed(2)} MB (-${((1 - totalMinifiedSize / totalOriginalSize) * 100).toFixed(1)}%)`,
862-
)
863-
logger.log(
864-
` Compressed: ${(totalCompressedSize / 1024 / 1024).toFixed(2)} MB (-${((1 - totalCompressedSize / totalOriginalSize) * 100).toFixed(1)}% total)`,
865-
)
866-
logger.log(
867-
` Final savings: ${((totalOriginalSize - totalCompressedSize) / 1024 / 1024).toFixed(2)} MB`,
868-
)
869-
logger.log('')
870-
871-
// Create decompression bootstrap hook.
872-
await createBrotliBootstrapHook()
873-
}
874-
875-
/**
876-
* Create bootstrap hook to transparently decompress Brotli-compressed JS files.
877-
*
878-
* This creates a module loader hook that intercepts require() calls and provides
879-
* transparent decompression of .js.br files. The hook is:
880-
* 1. Zero-overhead for non-compressed files (original _load still used).
881-
* 2. Cached in memory after first decompression (no repeated decompression).
882-
* 3. Fallback-safe (if .br doesn't exist, original error is thrown).
883-
*
884-
* How it works:
885-
* 1. Hook Module._load to intercept all module loading.
886-
* 2. Try normal loading first (zero overhead for uncompressed modules).
887-
* 3. If MODULE_NOT_FOUND, check for .js.br version.
888-
* 4. If found, decompress, cache, and compile the source.
889-
* 5. Return module exports as if it were a normal .js file.
890-
*
891-
* Performance:
892-
* - First load: ~1-2ms decompression overhead per module.
893-
* - Subsequent loads: <0.1ms from memory cache.
894-
* - Uncompressed files: Zero overhead (original code path).
895-
*/
896-
async function createBrotliBootstrapHook() {
897-
logger.log('Creating Brotli decompression bootstrap...')
898-
899-
const bootstrapCode = `
900-
// Brotli decompression hook for transparently loading compressed Node.js modules.
901-
// This hook intercepts Module._load and provides automatic decompression of .js.br files.
902-
(function() {
903-
const Module = require('internal/modules/cjs/loader').Module
904-
const fs = require('fs')
905-
const zlib = require('zlib')
906-
const originalLoad = Module._load
907-
const cache = new Map() // In-memory cache to avoid repeated decompression.
908-
909-
// Intercept module loading to add Brotli decompression support.
910-
Module._load = function(request, parent, isMain) {
911-
try {
912-
// First, try normal loading (zero overhead for uncompressed modules).
913-
return originalLoad.call(this, request, parent, isMain)
914-
} catch (e) {
915-
// If module not found, try loading .br compressed version.
916-
if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_REQUIRE_ESM') {
917-
try {
918-
// Resolve the filename to get absolute path.
919-
const filename = Module._resolveFilename(request, parent, isMain)
920-
const brFile = filename + '.br'
921-
922-
if (fs.existsSync(brFile)) {
923-
// Check memory cache first to avoid repeated decompression.
924-
if (cache.has(brFile)) {
925-
const module = new Module(filename, parent)
926-
module._compile(cache.get(brFile), filename)
927-
return module.exports
928-
}
929-
930-
// Decompress the .br file and cache the source in memory.
931-
const compressed = fs.readFileSync(brFile)
932-
const decompressed = zlib.brotliDecompressSync(compressed).toString('utf8')
933-
cache.set(brFile, decompressed)
934-
935-
// Compile and execute the decompressed source.
936-
const module = new Module(filename, parent)
937-
module._compile(decompressed, filename)
938-
return module.exports
939-
}
940-
} catch (brError) {
941-
// Fall through to original error if decompression fails.
942-
}
943-
}
944-
// Re-throw original error if no .br file found or decompression failed.
945-
throw e
946-
}
947-
}
948-
})()
949-
`
950-
951-
// Write the bootstrap hook to lib/internal/bootstrap/brotli-loader.js.
952-
const bootstrapFile = join(
953-
NODE_DIR,
954-
'lib',
955-
'internal',
956-
'bootstrap',
957-
'brotli-loader.js',
958-
)
959-
await writeFile(bootstrapFile, bootstrapCode, 'utf8')
960-
961-
// Inject the hook into Node.js's main bootstrap sequence.
962-
// This ensures the decompression hook is loaded before any modules are required.
963-
const mainBootstrap = join(
964-
NODE_DIR,
965-
'lib',
966-
'internal',
967-
'bootstrap',
968-
'node.js',
969-
)
970-
let content = await readFile(mainBootstrap, 'utf8')
971-
972-
// Check if already injected (for idempotency).
973-
if (!content.includes('brotli-loader')) {
974-
// Insert after 'use strict' directive at the top of the file.
975-
// This runs the hook early in the bootstrap sequence.
976-
const insertPoint = content.indexOf("'use strict';")
977-
if (insertPoint !== -1) {
978-
const endOfLine = content.indexOf('\n', insertPoint)
979-
content =
980-
content.slice(0, endOfLine + 1) +
981-
"\nrequire('internal/bootstrap/brotli-loader');\n" +
982-
content.slice(endOfLine + 1)
983-
await writeFile(mainBootstrap, content, 'utf8')
984-
logger.log(` ${colors.green('✓')} Injected Brotli loader into bootstrap`)
985-
}
986-
}
987-
988-
logger.log(`${colors.green('✓')} Brotli bootstrap hook created`)
989-
logger.log('')
990-
}
991721

992722
/**
993723
* Main build function.

0 commit comments

Comments
 (0)