Skip to content

Commit 947a6ee

Browse files
committed
Extract timing and configuration constants in install-npm-packages
1 parent 03caff8 commit 947a6ee

1 file changed

Lines changed: 67 additions & 104 deletions

File tree

scripts/install-npm-packages.mjs

Lines changed: 67 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ import { c as tarCreate, x as tarExtract } from 'tar'
7272
import { parseArgs } from '../registry/dist/lib/parse-args.js'
7373

7474
import { cleanTestScript } from '../test/utils/script-cleaning.mjs'
75-
import { testRunners } from '../test/utils/test-runners.mjs'
7675
import { suppressMaxListenersWarning } from './utils/suppress-warnings.mjs'
7776
import { safeRemove } from './utils/fs.mjs'
7877
import { filterPackagesByChanges } from './utils/git.mjs'
@@ -83,13 +82,34 @@ import {
8382
import constants from './constants.mjs'
8483
import ENV from '../registry/dist/lib/constants/ENV.js'
8584
import spinner from '../registry/dist/lib/constants/spinner.js'
85+
import NODE_MODULES from '../registry/dist/lib/constants/NODE_MODULES.js'
86+
import PACKAGE_JSON from '../registry/dist/lib/constants/PACKAGE_JSON.js'
8687
import WIN32 from '../registry/dist/lib/constants/WIN32.js'
8788
import { readPackageJson } from '../registry/dist/lib/packages.js'
8889
import { pEach, pRetry } from '../registry/dist/lib/promises.js'
8990
import { LOG_SYMBOLS, logger } from '../registry/dist/lib/logger.js'
9091
import { spawn } from '../registry/dist/lib/spawn.js'
9192
import { pluralize } from '../registry/dist/lib/words.js'
92-
import { writeJson } from '../registry/dist/lib/fs.js'
93+
import { readFileUtf8, readJson, writeJson } from '../registry/dist/lib/fs.js'
94+
95+
// Default concurrency values based on environment and platform.
96+
const DEFAULT_CI_CONCURRENCY_WIN32 = '5'
97+
const DEFAULT_CI_CONCURRENCY_POSIX = '10'
98+
const DEFAULT_DEV_CONCURRENCY = '15'
99+
100+
// Filesystem delay constants for tar extraction and JSON parsing.
101+
const FS_FLUSH_DELAY_MS = 100
102+
const JSON_PARSE_RETRY_BASE_DELAY_MS = 200
103+
const JSON_PARSE_MAX_RETRIES = 3
104+
105+
// Output truncation length for error messages.
106+
const ERROR_OUTPUT_TRUNCATE_LENGTH = 1_000
107+
108+
// Progress update intervals for CI vs. local environments.
109+
const PROGRESS_UPDATE_INTERVAL_CI = 10
110+
const PROGRESS_UPDATE_INTERVAL_DEV = 1
111+
const PROGRESS_TIMER_INTERVAL_CI_MS = 1_000
112+
const PROGRESS_TIMER_INTERVAL_DEV_MS = 100
93113

94114
const { values: cliArgs } = parseArgs({
95115
options: {
@@ -99,7 +119,11 @@ const { values: cliArgs } = parseArgs({
99119
},
100120
concurrency: {
101121
type: 'string',
102-
default: ENV.CI ? (WIN32 ? '5' : '10') : '15',
122+
default: ENV.CI
123+
? WIN32
124+
? DEFAULT_CI_CONCURRENCY_WIN32
125+
: DEFAULT_CI_CONCURRENCY_POSIX
126+
: DEFAULT_DEV_CONCURRENCY,
103127
},
104128
'temp-dir': {
105129
type: 'string',
@@ -140,7 +164,7 @@ function completePackage() {
140164

141165
async function computeOverrideHash(overridePath) {
142166
try {
143-
const pkgJsonPath = path.join(overridePath, 'package.json')
167+
const pkgJsonPath = path.join(overridePath, PACKAGE_JSON)
144168
const pkgJson = await readPackageJson(pkgJsonPath)
145169
// Hash the dependencies to detect changes.
146170
const depsString = JSON.stringify({
@@ -217,7 +241,7 @@ async function generatePnpmOverrides(options) {
217241
}
218242

219243
const packagePath = path.join(npmPackagesDir, packageName)
220-
const pkgJsonPath = path.join(packagePath, 'package.json')
244+
const pkgJsonPath = path.join(packagePath, PACKAGE_JSON)
221245

222246
try {
223247
// eslint-disable-next-line no-await-in-loop
@@ -322,7 +346,7 @@ async function applySocketOverrideIfExists(packageName, packagePath) {
322346
}
323347

324348
// Read the Socket override package.json.
325-
const overridePkgJsonPath = path.join(overridePath, 'package.json')
349+
const overridePkgJsonPath = path.join(overridePath, PACKAGE_JSON)
326350
let overridePkgJson
327351
try {
328352
overridePkgJson = await readPackageJson(overridePkgJsonPath)
@@ -331,7 +355,7 @@ async function applySocketOverrideIfExists(packageName, packagePath) {
331355
}
332356

333357
// Read the existing package.json.
334-
const packageJsonPath = path.join(packagePath, 'package.json')
358+
const packageJsonPath = path.join(packagePath, PACKAGE_JSON)
335359
let existingPkgJson
336360
try {
337361
existingPkgJson = await readPackageJson(packageJsonPath, {
@@ -352,9 +376,9 @@ async function applySocketOverrideIfExists(packageName, packagePath) {
352376
errorOnExist: false,
353377
...(WIN32 ? { retryDelay: 100, maxRetries: 3 } : {}),
354378
filter: src =>
355-
!src.includes('node_modules') &&
379+
!src.includes(NODE_MODULES) &&
356380
!src.endsWith('.DS_Store') &&
357-
!src.endsWith('package.json'),
381+
!src.endsWith(PACKAGE_JSON),
358382
})
359383
} catch (e) {
360384
// Ignore errors about same paths - this happens when pnpm symlinks to our override.
@@ -421,8 +445,8 @@ async function installPackage(packageInfo) {
421445
const packageTempDir = path.join(tempBaseDir, socketPkgName)
422446

423447
// Check if package is already installed and has a test script.
424-
const installedPath = path.join(packageTempDir, 'node_modules', origPkgName)
425-
const packageJsonPath = path.join(installedPath, 'package.json')
448+
const installedPath = path.join(packageTempDir, NODE_MODULES, origPkgName)
449+
const packageJsonPath = path.join(installedPath, PACKAGE_JSON)
426450
const installMarkerPath = path.join(
427451
packageTempDir,
428452
'.socket-install-complete',
@@ -438,9 +462,7 @@ async function installPackage(packageInfo) {
438462
const existingPkgJson = await readPackageJson(packageJsonPath, {
439463
editable: true,
440464
})
441-
const markerData = JSON.parse(
442-
await fs.readFile(installMarkerPath, 'utf8'),
443-
)
465+
const markerData = await readJson(installMarkerPath)
444466

445467
// Verify the installation matches the requested version and override hash.
446468
// Also check if the cached installation references GitHub extraction directories.
@@ -620,25 +642,29 @@ async function installPackage(packageInfo) {
620642

621643
// Wait briefly for filesystem to flush after tar extraction.
622644
// This prevents reading truncated files on slower CI systems.
623-
await new Promise(resolve => setTimeout(resolve, 100))
645+
await new Promise(resolve => setTimeout(resolve, FS_FLUSH_DELAY_MS))
624646

625647
// Find the extracted directory (GitHub archives extract to reponame-commitish/).
626648
const entries = await fs.readdir(tempExtractDir, {
627649
withFileTypes: true,
628650
})
629651
const extractedDir = entries.find(
630-
e => e.isDirectory() && e.name !== 'node_modules',
652+
e => e.isDirectory() && e.name !== NODE_MODULES,
631653
)
632654

633655
if (extractedDir) {
634656
const extractedPath = path.join(tempExtractDir, extractedDir.name)
635-
const pkgJsonPath = path.join(extractedPath, 'package.json')
657+
const pkgJsonPath = path.join(extractedPath, PACKAGE_JSON)
636658

637659
// Verify package.json exists and is not empty.
638-
// Retry up to 3 times to handle filesystem flush delays on slow CI systems.
660+
// Retry up to JSON_PARSE_MAX_RETRIES times to handle filesystem flush delays on slow CI systems.
639661
let editablePkgJson
640662
let lastError
641-
for (let attempt = 1; attempt <= 3; attempt += 1) {
663+
for (
664+
let attempt = 1;
665+
attempt <= JSON_PARSE_MAX_RETRIES;
666+
attempt += 1
667+
) {
642668
try {
643669
// eslint-disable-next-line no-await-in-loop
644670
const pkgJsonStats = await fs.stat(pkgJsonPath)
@@ -655,18 +681,20 @@ async function installPackage(packageInfo) {
655681
break
656682
} catch (error) {
657683
lastError = error
658-
if (attempt < 3) {
684+
if (attempt < JSON_PARSE_MAX_RETRIES) {
659685
// Wait longer on each retry (200ms, 400ms).
660686
// eslint-disable-next-line no-await-in-loop
661-
await new Promise(resolve => setTimeout(resolve, attempt * 200))
687+
await new Promise(resolve =>
688+
setTimeout(resolve, attempt * JSON_PARSE_RETRY_BASE_DELAY_MS),
689+
)
662690
}
663691
}
664692
}
665693

666694
if (!editablePkgJson) {
667695
// All retries failed, add diagnostic info.
668696
const pkgJsonStats = await fs.stat(pkgJsonPath)
669-
const fileContent = await fs.readFile(pkgJsonPath, 'utf8')
697+
const fileContent = await readFileUtf8(pkgJsonPath)
670698
throw new Error(
671699
`Invalid package.json after 3 retries: ${lastError.message}. File size: ${pkgJsonStats.size}, Content preview: ${fileContent.slice(0, 200)}`,
672700
)
@@ -901,11 +929,6 @@ async function installPackage(packageInfo) {
901929
...(overridePkgJson.main ? { main: overridePkgJson.main } : {}),
902930
...(overridePkgJson.module ? { module: overridePkgJson.module } : {}),
903931
...(overridePkgJson.types ? { types: overridePkgJson.types } : {}),
904-
...(overridePkgJson.files ? { files: overridePkgJson.files } : {}),
905-
...(overridePkgJson.sideEffects !== undefined
906-
? { sideEffects: overridePkgJson.sideEffects }
907-
: {}),
908-
...(overridePkgJson.socket ? { socket: overridePkgJson.socket } : {}),
909932
// Make the package private for testing.
910933
private: true,
911934
})
@@ -916,86 +939,18 @@ async function installPackage(packageInfo) {
916939
delete originalPkgJson.content.scripts.pretest
917940
delete originalPkgJson.content.scripts.posttest
918941

919-
// Look for actual test runner in scripts.
920-
const additionalTestRunners = [...testRunners, 'test:stock', 'test:all']
921-
let actualTestScript = additionalTestRunners.find(
922-
runner => originalPkgJson.content.scripts[runner],
923-
)
924-
925-
if (!actualTestScript && originalPkgJson.content.scripts.test) {
926-
// Try to extract the test runner from the test script.
927-
const testMatch =
928-
originalPkgJson.content.scripts.test.match(/npm run ([-:\w]+)/)
929-
if (testMatch && originalPkgJson.content.scripts[testMatch[1]]) {
930-
actualTestScript = testMatch[1]
931-
}
932-
}
933-
934-
// Helper to check if a script contains non-test patterns.
935-
const nonTestPatterns = ['lint', 'pretest', 'build', 'compile', 'tsc']
936-
const containsNonTestPattern = script =>
937-
nonTestPatterns.some(pattern => script?.includes(pattern))
938-
939-
// Helper to find a real test runner that doesn't run non-test commands.
940-
const findRealTestRunner = () =>
941-
testRunners.find(
942-
runner =>
943-
originalPkgJson.content.scripts[runner] &&
944-
!containsNonTestPattern(originalPkgJson.content.scripts[runner]),
945-
)
946-
947942
// Build cleaned scripts object.
948943
const cleanedScripts = { __proto__: null }
949944
for (const { 0: key, 1: value } of Object.entries(
950945
originalPkgJson.content.scripts,
951946
)) {
952-
if (key.startsWith('test') || key === actualTestScript) {
947+
if (key.startsWith('test')) {
953948
cleanedScripts[key] = cleanTestScript(value)
954949
} else {
955950
cleanedScripts[key] = value
956951
}
957952
}
958953

959-
// Clean the test script first to extract just the test runner.
960-
if (cleanedScripts.test) {
961-
// If the test script just runs non-test commands after cleaning, find a real test runner.
962-
if (containsNonTestPattern(cleanedScripts.test)) {
963-
const realTestRunner = findRealTestRunner()
964-
if (realTestRunner) {
965-
cleanedScripts.test =
966-
originalPkgJson.content.scripts[realTestRunner]
967-
}
968-
}
969-
970-
// If test script just delegates to another script, resolve it.
971-
const delegateMatch = cleanedScripts.test?.match(/^npm run ([-:\w]+)$/)
972-
if (delegateMatch) {
973-
const targetScript = delegateMatch[1]
974-
const targetScriptContent =
975-
originalPkgJson.content.scripts[targetScript]
976-
977-
if (
978-
targetScriptContent &&
979-
!containsNonTestPattern(targetScriptContent)
980-
) {
981-
// Resolve to the actual command, not the npm run wrapper.
982-
cleanedScripts.test = targetScriptContent
983-
// Also preserve the target script itself.
984-
cleanedScripts[targetScript] = targetScriptContent
985-
} else {
986-
// Target script doesn't exist or is non-test, try to find a real test runner.
987-
const realTestRunner = findRealTestRunner()
988-
if (realTestRunner) {
989-
cleanedScripts.test =
990-
originalPkgJson.content.scripts[realTestRunner]
991-
// Also preserve the real test runner script.
992-
cleanedScripts[realTestRunner] =
993-
originalPkgJson.content.scripts[realTestRunner]
994-
}
995-
}
996-
}
997-
}
998-
999954
// Update scripts using .update().
1000955
originalPkgJson.update({
1001956
scripts: cleanedScripts,
@@ -1082,15 +1037,21 @@ async function installPackage(packageInfo) {
10821037
writeProgress(LOG_SYMBOLS.fail)
10831038
completePackage()
10841039
const errorDetails = [error.message]
1085-
// Show last 1000 chars of stderr (where actual errors appear)
1040+
// Show last ERROR_OUTPUT_TRUNCATE_LENGTH chars of stderr (where actual errors appear).
10861041
if (error.stderr) {
1087-
const stderrText = error.stderr.slice(-1_000)
1088-
errorDetails.push('STDERR (last 1000 chars):', stderrText)
1042+
const stderrText = error.stderr.slice(-ERROR_OUTPUT_TRUNCATE_LENGTH)
1043+
errorDetails.push(
1044+
`STDERR (last ${ERROR_OUTPUT_TRUNCATE_LENGTH} chars):`,
1045+
stderrText,
1046+
)
10891047
}
1090-
// Show last 1000 chars of stdout
1048+
// Show last ERROR_OUTPUT_TRUNCATE_LENGTH chars of stdout.
10911049
if (error.stdout) {
1092-
const stdoutText = error.stdout.slice(-1_000)
1093-
errorDetails.push('STDOUT (last 1000 chars):', stdoutText)
1050+
const stdoutText = error.stdout.slice(-ERROR_OUTPUT_TRUNCATE_LENGTH)
1051+
errorDetails.push(
1052+
`STDOUT (last ${ERROR_OUTPUT_TRUNCATE_LENGTH} chars):`,
1053+
stdoutText,
1054+
)
10941055
}
10951056
return {
10961057
package: origPkgName,
@@ -1194,7 +1155,9 @@ async function main() {
11941155

11951156
// Update spinner text when progress changes.
11961157
// In CI environments, batch updates to avoid excessive line output.
1197-
const updateInterval = ENV.CI ? 10 : 1
1158+
const updateInterval = ENV.CI
1159+
? PROGRESS_UPDATE_INTERVAL_CI
1160+
: PROGRESS_UPDATE_INTERVAL_DEV
11981161
let lastCompletedCount = 0
11991162
const progressInterval = setInterval(
12001163
() => {
@@ -1209,7 +1172,7 @@ async function main() {
12091172
lastCompletedCount = completedPackages
12101173
}
12111174
},
1212-
ENV.CI ? 1_000 : 100,
1175+
ENV.CI ? PROGRESS_TIMER_INTERVAL_CI_MS : PROGRESS_TIMER_INTERVAL_DEV_MS,
12131176
)
12141177

12151178
// Ensure base temp directory exists.

0 commit comments

Comments
 (0)