np-audit 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,11 +40,21 @@ async function runAware(opts) {
40
40
 
41
41
  let cursor = 0;
42
42
 
43
+ // Use alternate screen buffer on supported terminals (not legacy Windows console)
44
+ const useAltScreen = process.stdout.isTTY && (
45
+ process.platform !== 'win32' ||
46
+ process.env.WT_SESSION || // Windows Terminal
47
+ process.env.ConEmuPID // ConEmu
48
+ );
49
+
50
+ if (useAltScreen) {
51
+ process.stdout.write('\x1b[?1049h');
52
+ }
53
+
43
54
  function render() {
44
- // Move cursor to top of list — use ANSI escape to clear + redraw
45
- process.stdout.write('\x1b[2J\x1b[H'); // clear screen
55
+ process.stdout.write('\x1b[H\x1b[2J');
46
56
  output.log('');
47
- output.log(output.bold(' npa --aware mode'));
57
+ output.log(output.bold(' npa --review mode'));
48
58
  output.log(output.dim(' Use ↑/↓ to navigate, SPACE to toggle, ENTER to confirm, q to quit'));
49
59
  output.log('');
50
60
  output.log(` Found ${items.length} package(s) with install scripts:\n`);
@@ -115,8 +125,10 @@ async function runAware(opts) {
115
125
  process.stdin.on('keypress', onKey);
116
126
  });
117
127
 
118
- // Clear screen after TUI exits
119
- process.stdout.write('\x1b[2J\x1b[H');
128
+ // Exit alternate screen buffer (restores previous screen)
129
+ if (useAltScreen) {
130
+ process.stdout.write('\x1b[?1049l');
131
+ }
120
132
 
121
133
  const allowedItems = items.filter(i => i.allowed);
122
134
  const deniedItems = items.filter(i => !i.allowed);
@@ -124,30 +136,58 @@ async function runAware(opts) {
124
136
  output.log('');
125
137
  output.info(`Proceeding with ${allowedItems.length} allowed / ${deniedItems.length} denied`);
126
138
 
139
+ // Get names of denied packages to exclude from install
140
+ const deniedNames = new Set(deniedItems.map(i => i.result.pkg.name));
141
+
142
+ // Filter npmArgs to exclude denied packages
143
+ let filteredNpmArgs = npmArgs.filter(arg => {
144
+ // Extract package name (handle @scope/pkg and pkg@version formats)
145
+ const name = arg.startsWith('@')
146
+ ? arg.split('/').slice(0, 2).join('/').split('@').slice(0, 2).join('@').replace(/@[^@]*$/, '') || arg.split('@').slice(0, 2).join('@')
147
+ : arg.split('@')[0];
148
+ return !deniedNames.has(name);
149
+ });
150
+
151
+ // If all explicitly requested packages are denied, abort
152
+ if (npmArgs.length > 0 && filteredNpmArgs.length === 0) {
153
+ output.error('All requested packages were denied. Aborting install.');
154
+ return 1;
155
+ }
156
+
127
157
  if (deniedItems.length > 0) {
128
- output.warn('Running npm with --ignore-scripts (will run allowed scripts manually after)');
129
- const code = runNpm(command, [...npmArgs, '--ignore-scripts'], cwd);
130
- if (code !== 0) return code;
131
-
132
- for (const item of allowedItems) {
133
- const exitCode = runPackageScripts(item.result, cwd);
134
- if (exitCode !== 0) {
135
- output.error(`Script for ${item.result.pkg.name} exited with code ${exitCode}`);
158
+ if (filteredNpmArgs.length > 0 || npmArgs.length === 0) {
159
+ output.warn('Running npm with --ignore-scripts (will run allowed scripts manually after)');
160
+ const code = runNpm(command, [...filteredNpmArgs, '--ignore-scripts'], cwd);
161
+ if (code !== 0) return code;
162
+
163
+ for (const item of allowedItems) {
164
+ const exitCode = runPackageScripts(item.result, cwd);
165
+ if (exitCode !== 0) {
166
+ output.error(`Script for ${item.result.pkg.name} exited with code ${exitCode}`);
167
+ }
136
168
  }
169
+ return 0;
170
+ } else {
171
+ output.warn('No packages to install after excluding denied packages.');
172
+ return 0;
137
173
  }
138
- return 0;
139
174
  } else {
140
- return runNpm(command, npmArgs, cwd);
175
+ return runNpm(command, filteredNpmArgs.length > 0 ? filteredNpmArgs : npmArgs, cwd);
141
176
  }
142
177
  }
143
178
 
144
179
  /**
145
180
  * Spawn npm install/ci and return the exit code.
181
+ * Sets NPA_RUNNING=1 to prevent recursive hooks when npm is aliased to npa.
146
182
  */
147
183
  function runNpm(command, args, cwd) {
148
184
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
149
185
  const npmArgs = command === 'ci' ? ['ci', ...args] : ['install', ...args];
150
- const result = spawnSync(npmCmd, npmArgs, { stdio: 'inherit', cwd });
186
+ const result = spawnSync(npmCmd, npmArgs, {
187
+ stdio: 'inherit',
188
+ cwd,
189
+ env: { ...process.env, NPA_RUNNING: '1' },
190
+ });
151
191
  return result.status || 0;
152
192
  }
153
193
 
File without changes
File without changes
File without changes