diff --git a/.config/rollup.base.config.mjs b/.config/rollup.base.config.mjs index 5bd603eb..2a081939 100644 --- a/.config/rollup.base.config.mjs +++ b/.config/rollup.base.config.mjs @@ -293,8 +293,8 @@ function ${SOCKET_INTEROP}(e) { ).map(o => ({ ...o, chunkFileNames: '[name].js', - manualChunks: id => - id.includes(SLASH_NODE_MODULES_SLASH) ? 'vendor' : null + manualChunks: id_ => + normalizeId(id_).includes(SLASH_NODE_MODULES_SLASH) ? 'vendor' : null })) // Replace hard-coded absolute paths in source with hard-coded relative paths. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78023e51..3abb3da4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,5 @@ jobs: with: no-lockfile: true npm-test-script: 'test-ci' - node-versions: '20' - # We currently have some issues on Windows that will have to wait to be fixed - # os: 'ubuntu-latest,windows-latest' - os: 'ubuntu-latest' + node-versions: '20,22' + os: 'ubuntu-latest,windows-latest' diff --git a/package.json b/package.json index 21a6619a..988e7ff3 100644 --- a/package.json +++ b/package.json @@ -51,11 +51,11 @@ "lint:fix": "npm run lint -- --fix && npm run lint:fix:fast", "lint:fix:fast": "prettier --cache --log-level warn --write .", "prepare": "husky && custompatch", - "test": "run-s check build:* test:*", - "test:c8": "c8 --reporter=none node --test 'test/socket-npm.test.cjs'", + "test": "run-s check build:* test:* test:coverage:*", "test-ci": "run-s build:* test:*", "test:unit": "tap-run", - "test:coverage": "cp -r .tap/coverage/*.json coverage/tmp && c8 --reporter=lcov --reporter=text --include 'dist/{module-sync,require}/*.js' --exclude 'dist/require/vendor.js' report" + "test:coverage:c8": "c8 --reporter=none node --test 'test/socket-npm.test.cjs'", + "test:coverage:merge": "cp -r .tap/coverage/*.json coverage/tmp && c8 --reporter=lcov --reporter=text --include 'dist/{module-sync,require}/*.js' --exclude 'dist/require/vendor.js' report" }, "dependencies": { "@apideck/better-ajv-errors": "^0.3.6", diff --git a/src/commands/npm.ts b/src/commands/npm.ts index 580cd933..1a9ab501 100644 --- a/src/commands/npm.ts +++ b/src/commands/npm.ts @@ -6,7 +6,7 @@ import constants from '../constants' import type { CliSubcommand } from '../utils/meow-with-subcommands' -const { distPath } = constants +const { distPath, execPath } = constants const description = 'npm wrapper functionality' @@ -16,7 +16,7 @@ export const npm: CliSubcommand = { const wrapperPath = path.join(distPath, 'npm-cli.js') process.exitCode = 1 const spawnPromise = spawn( - process.execPath, + execPath, [ // Lazily access constants.nodeNoWarningsFlags. ...constants.nodeNoWarningsFlags, diff --git a/src/commands/npx.ts b/src/commands/npx.ts index 0f86cf64..795f61f0 100644 --- a/src/commands/npx.ts +++ b/src/commands/npx.ts @@ -6,7 +6,7 @@ import constants from '../constants' import type { CliSubcommand } from '../utils/meow-with-subcommands' -const { distPath } = constants +const { distPath, execPath } = constants const description = 'npx wrapper functionality' @@ -16,7 +16,7 @@ export const npx: CliSubcommand = { const wrapperPath = path.join(distPath, 'npx-cli.js') process.exitCode = 1 const spawnPromise = spawn( - process.execPath, + execPath, [ // Lazily access constants.nodeNoWarningsFlags. ...constants.nodeNoWarningsFlags, diff --git a/src/commands/optimize.ts b/src/commands/optimize.ts index ffd9a220..6559b486 100644 --- a/src/commands/optimize.ts +++ b/src/commands/optimize.ts @@ -42,7 +42,8 @@ import type { Spinner } from '@socketregistry/yocto-spinner' type PackageJson = Awaited> -const { UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE, distPath } = constants +const { UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE, distPath, execPath } = + constants const COMMAND_TITLE = 'Socket Optimize' const OVERRIDES_FIELD_NAME = 'overrides' @@ -902,7 +903,7 @@ export const optimize: CliSubcommand = { } } await spawn( - process.execPath, + execPath, [wrapperPath, 'install', '--silent'], npmSpawnOptions ) @@ -910,7 +911,7 @@ export const optimize: CliSubcommand = { // will error out after Socket Optimize generates a lock file. More // investigation is needed. await spawn( - process.execPath, + execPath, [ wrapperPath, 'install', diff --git a/src/shadow/npm-cli.ts b/src/shadow/npm-cli.ts index dbff8d21..fdf945af 100755 --- a/src/shadow/npm-cli.ts +++ b/src/shadow/npm-cli.ts @@ -9,7 +9,7 @@ import constants from '../constants' import { installLinks } from './link' import { findRoot } from '../utils/path-resolve' -const { distPath, shadowBinPath } = constants +const { distPath, execPath, shadowBinPath } = constants const npmPath = installLinks(shadowBinPath, 'npm') const injectionPath = path.join(distPath, 'npm-injection.js') @@ -40,7 +40,7 @@ if ( process.exitCode = 1 const spawnPromise = spawn( - process.execPath, + execPath, [ // Lazily access constants.nodeNoWarningsFlags. ...constants.nodeNoWarningsFlags, diff --git a/src/shadow/npx-cli.ts b/src/shadow/npx-cli.ts index b88e4d84..a2066c50 100755 --- a/src/shadow/npx-cli.ts +++ b/src/shadow/npx-cli.ts @@ -7,14 +7,14 @@ import spawn from '@npmcli/promise-spawn' import constants from '../constants' import { installLinks } from './link' -const { distPath, shadowBinPath } = constants +const { distPath, execPath, shadowBinPath } = constants const npxPath = installLinks(shadowBinPath, 'npx') const injectionPath = path.join(distPath, 'npm-injection.js') process.exitCode = 1 const spawnPromise = spawn( - process.execPath, + execPath, [ // Lazily access constants.nodeNoWarningsFlags. ...constants.nodeNoWarningsFlags, diff --git a/test/path-resolve.test.ts b/test/path-resolve.test.ts index 8ab90e6b..24e94ffb 100644 --- a/test/path-resolve.test.ts +++ b/test/path-resolve.test.ts @@ -5,10 +5,12 @@ import { afterEach, beforeEach, describe, it } from 'node:test' import mockFs from 'mock-fs' import nock from 'nock' +import { normalizePath } from '@socketsecurity/registry/lib/path' + import { getPackageFiles } from './dist/path-resolve' const testPath = __dirname -const mockPath = path.join(testPath, 'mock') +const mockPath = normalizePath(path.join(testPath, 'mock')) const globPatterns = { general: { @@ -88,10 +90,13 @@ describe('Path Resolve', () => { [`${mockPath}/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles(mockPath, ['.'], undefined, globPatterns), - [`${mockPath}/package.json`] + const actual = await sortedGetPackageFiles( + mockPath, + ['.'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [`${mockPath}/package.json`]) }) it('should respect ignores from socket config', async () => { @@ -102,24 +107,22 @@ describe('Path Resolve', () => { [`${mockPath}/foo/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - { - version: 2, - projectIgnorePaths: ['bar/*', '!bar/package.json'], - issueRules: {}, - githubApp: {} - }, - globPatterns - ), - [ - `${mockPath}/bar/package.json`, - `${mockPath}/foo/package-lock.json`, - `${mockPath}/foo/package.json` - ] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + { + version: 2, + projectIgnorePaths: ['bar/*', '!bar/package.json'], + issueRules: {}, + githubApp: {} + }, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [ + `${mockPath}/bar/package.json`, + `${mockPath}/foo/package-lock.json`, + `${mockPath}/foo/package.json` + ]) }) it('should respect .gitignore', async () => { @@ -131,19 +134,17 @@ describe('Path Resolve', () => { [`${mockPath}/foo/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [ - `${mockPath}/bar/package.json`, - `${mockPath}/foo/package-lock.json`, - `${mockPath}/foo/package.json` - ] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [ + `${mockPath}/bar/package.json`, + `${mockPath}/foo/package-lock.json`, + `${mockPath}/foo/package.json` + ]) }) it('should always ignore some paths', async () => { @@ -162,15 +163,16 @@ describe('Path Resolve', () => { [`${mockPath}/foo/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [`${mockPath}/foo/package-lock.json`, `${mockPath}/foo/package.json`] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [ + `${mockPath}/foo/package-lock.json`, + `${mockPath}/foo/package.json` + ]) }) it('should ignore irrelevant matches', async () => { @@ -181,15 +183,16 @@ describe('Path Resolve', () => { [`${mockPath}/foo/random.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [`${mockPath}/foo/package-lock.json`, `${mockPath}/foo/package.json`] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [ + `${mockPath}/foo/package-lock.json`, + `${mockPath}/foo/package.json` + ]) }) it('should be lenient on oddities', async () => { @@ -199,15 +202,13 @@ describe('Path Resolve', () => { } }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), []) }) it('should resolve package and lock file', async () => { @@ -216,15 +217,16 @@ describe('Path Resolve', () => { [`${mockPath}/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [`${mockPath}/package-lock.json`, `${mockPath}/package.json`] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [ + `${mockPath}/package-lock.json`, + `${mockPath}/package.json` + ]) }) it('should resolve package without lock file', async () => { @@ -232,15 +234,13 @@ describe('Path Resolve', () => { [`${mockPath}/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [`${mockPath}/package.json`] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [`${mockPath}/package.json`]) }) it('should support alternative lock files', async () => { @@ -249,15 +249,16 @@ describe('Path Resolve', () => { [`${mockPath}/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [`${mockPath}/package.json`, `${mockPath}/yarn.lock`] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [ + `${mockPath}/package.json`, + `${mockPath}/yarn.lock` + ]) }) it('should handle all variations', async () => { @@ -271,23 +272,21 @@ describe('Path Resolve', () => { [`${mockPath}/abc/package.json`]: '{}' }) - assert.deepEqual( - await sortedGetPackageFiles( - mockPath, - ['**/*'], - undefined, - globPatterns - ), - [ - `${mockPath}/abc/package.json`, - `${mockPath}/bar/package.json`, - `${mockPath}/bar/yarn.lock`, - `${mockPath}/foo/package-lock.json`, - `${mockPath}/foo/package.json`, - `${mockPath}/package-lock.json`, - `${mockPath}/package.json` - ] + const actual = await sortedGetPackageFiles( + mockPath, + ['**/*'], + undefined, + globPatterns ) + assert.deepEqual(actual.map(normalizePath), [ + `${mockPath}/abc/package.json`, + `${mockPath}/bar/package.json`, + `${mockPath}/bar/yarn.lock`, + `${mockPath}/foo/package-lock.json`, + `${mockPath}/foo/package.json`, + `${mockPath}/package-lock.json`, + `${mockPath}/package.json` + ]) }) }) }) diff --git a/test/socket-cdxgen.test.ts b/test/socket-cdxgen.test.ts index 0481ac5a..655f3535 100644 --- a/test/socket-cdxgen.test.ts +++ b/test/socket-cdxgen.test.ts @@ -1,4 +1,5 @@ import assert from 'node:assert/strict' +import path from 'node:path' import { describe, it } from 'node:test' import spawn from '@npmcli/promise-spawn' @@ -9,10 +10,14 @@ type PromiseSpawnOptions = Exclude[2], undefined> & { encoding?: BufferEncoding | undefined } -const { distPath } = constants +const { distPath, execPath } = constants + +const entryPath = path.join(distPath, 'cli.js') +const testPath = __dirname +const npmFixturesPath = path.join(testPath, 'socket-npm-fixtures') const spawnOpts: PromiseSpawnOptions = { - cwd: distPath, + cwd: npmFixturesPath, encoding: 'utf8' } @@ -20,21 +25,31 @@ describe('Socket cdxgen command', async () => { it('should forwards known commands to cdxgen', async () => { for (const command of ['-h', '--help']) { // eslint-disable-next-line no-await-in-loop - const ret = await spawn('./cli.js', ['cdxgen', command], spawnOpts) - assert.ok(ret.stdout.startsWith('cdxgen'), 'forwards commands to cdxgen') + const ret = await spawn( + execPath, + [entryPath, 'cdxgen', command], + spawnOpts + ) + assert.deepStrictEqual({ stdin: ret.stdin, stdout: ret.stdout }, {}, JSON.stringify({ stdin: ret.stdin, stdout: ret.stdout })) + // assert.ok(ret.stdout.startsWith('cdxgen'), 'forwards commands to cdxgen') } }) it('should not forward unknown commands to cdxgen', async () => { for (const command of ['-u', '--unknown']) { // eslint-disable-next-line no-await-in-loop await assert.rejects( - () => spawn('./cli.js', ['cdxgen', command], spawnOpts), + () => spawn(execPath, [entryPath, 'cdxgen', command], spawnOpts), e => e?.['stderr']?.startsWith(`Unknown argument: ${command}`), 'singular' ) } await assert.rejects( - () => spawn('./cli.js', ['cdxgen', '-u', '-h', '--unknown'], spawnOpts), + () => + spawn( + execPath, + [entryPath, 'cdxgen', '-u', '-h', '--unknown'], + spawnOpts + ), e => e?.['stderr']?.startsWith('Unknown arguments: -u, --unknown'), 'plural' ) diff --git a/test/socket-npm.test.cjs b/test/socket-npm.test.cjs index 88634df4..a69657ea 100644 --- a/test/socket-npm.test.cjs +++ b/test/socket-npm.test.cjs @@ -8,10 +8,10 @@ const { describe, it } = require('node:test') const spawn = require('@npmcli/promise-spawn') const constants = require('../scripts/constants') -const { distPath } = constants +const { distPath, execPath } = constants -const testPath = __dirname const entryPath = path.join(distPath, 'cli.js') +const testPath = __dirname const npmFixturesPath = path.join(testPath, 'socket-npm-fixtures') // These aliases are defined in package.json. @@ -28,7 +28,7 @@ for (const npm of ['npm8', 'npm10']) { it('should bail on new typosquat', async () => { await assert.rejects( () => - spawn(process.execPath, [entryPath, 'npm', 'install', 'bowserify'], { + spawn(execPath, [entryPath, 'npm', 'install', 'bowserify'], { cwd: path.join(npmFixturesPath, 'lacking-typosquat'), encoding: 'utf8', env: {