@netlify/build 29.44.0 → 29.45.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.
package/lib/core/build.js CHANGED
@@ -296,6 +296,7 @@ const initAndRunBuild = async function ({ pluginsOptions, netlifyConfig, configO
296
296
  timers: timersA,
297
297
  featureFlags,
298
298
  quiet,
299
+ systemLog,
299
300
  systemLogFile,
300
301
  });
301
302
  try {
@@ -3,6 +3,7 @@ import { addErrorInfo } from '../error/info.js';
3
3
  import { addPluginLoadErrorStatus } from '../status/load_error.js';
4
4
  import { measureDuration } from '../time/main.js';
5
5
  import { callChild } from './ipc.js';
6
+ import { captureStandardError } from './system_log.js';
6
7
  const pSetTimeout = promisify(setTimeout);
7
8
  // Retrieve all plugins steps
8
9
  // Can use either a module name or a file path to the plugin.
@@ -44,16 +45,7 @@ const loadAllPlugins = measureDuration(tLoadAllPlugins, 'load_plugins');
44
45
  const loadPlugin = async function ({ packageName, pluginPackageJson, pluginPackageJson: { version } = {}, pluginPath, inputs, loadedFrom, origin }, { childProcesses, index, packageJson, logs, debug, verbose, netlifyConfig, featureFlags, systemLog }) {
45
46
  const { childProcess } = childProcesses[index];
46
47
  const loadEvent = 'load';
47
- // A buffer for any data piped into the child process' stderr. We'll pipe
48
- // this to system logs if we fail to load the plugin.
49
- const bufferedStdErr = [];
50
- let bufferedStdListener;
51
- if (featureFlags.netlify_build_plugin_system_log && childProcess.stderr) {
52
- bufferedStdListener = (data) => {
53
- bufferedStdErr.push(data.toString().trimEnd());
54
- };
55
- childProcess.stderr.on('data', bufferedStdListener);
56
- }
48
+ const cleanup = captureStandardError(childProcess, systemLog, loadEvent, featureFlags);
57
49
  try {
58
50
  const { events } = await callChild({
59
51
  childProcess,
@@ -76,9 +68,6 @@ const loadPlugin = async function ({ packageName, pluginPackageJson, pluginPacka
76
68
  if (featureFlags.netlify_build_plugin_system_log) {
77
69
  // Wait for stderr to be flushed.
78
70
  await pSetTimeout(0);
79
- bufferedStdErr.forEach((line) => {
80
- systemLog(line);
81
- });
82
71
  }
83
72
  addErrorInfo(error, {
84
73
  plugin: { packageName, pluginPackageJson },
@@ -88,8 +77,6 @@ const loadPlugin = async function ({ packageName, pluginPackageJson, pluginPacka
88
77
  throw error;
89
78
  }
90
79
  finally {
91
- if (bufferedStdListener) {
92
- childProcess.stderr.removeListener('data', bufferedStdListener);
93
- }
80
+ cleanup();
94
81
  }
95
82
  };
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from 'module';
2
2
  import { fileURLToPath, pathToFileURL } from 'url';
3
+ import { promisify } from 'util';
3
4
  import { trace } from '@opentelemetry/api';
4
5
  import { execaNode } from 'execa';
5
6
  import { gte } from 'semver';
@@ -9,7 +10,9 @@ import { isTrustedPlugin } from '../steps/plugin.js';
9
10
  import { measureDuration } from '../time/main.js';
10
11
  import { callChild, getEventFromChild } from './ipc.js';
11
12
  import { getSpawnInfo } from './options.js';
13
+ import { captureStandardError } from './system_log.js';
12
14
  const CHILD_MAIN_FILE = fileURLToPath(new URL('child/main.js', import.meta.url));
15
+ const pSetTimeout = promisify(setTimeout);
13
16
  const require = createRequire(import.meta.url);
14
17
  // Start child processes used by all plugins
15
18
  // We fire plugins through child processes so that:
@@ -17,7 +20,7 @@ const require = createRequire(import.meta.url);
17
20
  // (for both security and safety reasons)
18
21
  // - logs can be buffered which allows manipulating them for log shipping,
19
22
  // transforming and parallel plugins
20
- const tStartPlugins = async function ({ pluginsOptions, buildDir, childEnv, logs, debug, quiet, systemLogFile }) {
23
+ const tStartPlugins = async function ({ pluginsOptions, buildDir, childEnv, logs, debug, quiet, systemLog, systemLogFile, featureFlags, }) {
21
24
  if (!quiet) {
22
25
  logRuntime(logs, pluginsOptions);
23
26
  logLoadingPlugins(logs, pluginsOptions, debug);
@@ -25,11 +28,21 @@ const tStartPlugins = async function ({ pluginsOptions, buildDir, childEnv, logs
25
28
  }
26
29
  logOutdatedPlugins(logs, pluginsOptions);
27
30
  logIncompatiblePlugins(logs, pluginsOptions);
28
- const childProcesses = await Promise.all(pluginsOptions.map(({ pluginDir, nodePath, nodeVersion, pluginPackageJson }) => startPlugin({ pluginDir, nodePath, nodeVersion, buildDir, childEnv, systemLogFile, pluginPackageJson })));
31
+ const childProcesses = await Promise.all(pluginsOptions.map(({ pluginDir, nodePath, nodeVersion, pluginPackageJson }) => startPlugin({
32
+ pluginDir,
33
+ nodePath,
34
+ nodeVersion,
35
+ buildDir,
36
+ childEnv,
37
+ systemLog,
38
+ systemLogFile,
39
+ pluginPackageJson,
40
+ featureFlags,
41
+ })));
29
42
  return { childProcesses };
30
43
  };
31
44
  export const startPlugins = measureDuration(tStartPlugins, 'start_plugins');
32
- const startPlugin = async function ({ pluginDir, nodeVersion, nodePath, buildDir, childEnv, systemLogFile, pluginPackageJson, }) {
45
+ const startPlugin = async function ({ pluginDir, nodeVersion, nodePath, buildDir, childEnv, systemLog, systemLogFile, pluginPackageJson, featureFlags, }) {
33
46
  const ctx = trace.getActiveSpan()?.spanContext();
34
47
  // the baggage will be passed to the child process when sending the run event
35
48
  const args = [
@@ -71,15 +84,24 @@ const startPlugin = async function ({ pluginDir, nodeVersion, nodePath, buildDir
71
84
  ? ['pipe', 'pipe', 'pipe', 'ipc', systemLogFile]
72
85
  : undefined,
73
86
  });
87
+ const readyEvent = 'ready';
88
+ const cleanup = captureStandardError(childProcess, systemLog, readyEvent, featureFlags);
74
89
  try {
75
- await getEventFromChild(childProcess, 'ready');
90
+ await getEventFromChild(childProcess, readyEvent);
76
91
  return { childProcess };
77
92
  }
78
93
  catch (error) {
94
+ if (featureFlags.netlify_build_plugin_system_log) {
95
+ // Wait for stderr to be flushed.
96
+ await pSetTimeout(0);
97
+ }
79
98
  const spawnInfo = getSpawnInfo();
80
99
  addErrorInfo(error, spawnInfo);
81
100
  throw error;
82
101
  }
102
+ finally {
103
+ cleanup();
104
+ }
83
105
  };
84
106
  // Stop all plugins child processes
85
107
  export const stopPlugins = async function ({ childProcesses, logs, verbose, pluginOptions, netlifyConfig, }) {
@@ -0,0 +1,4 @@
1
+ import type { FeatureFlags } from '../core/feature_flags.js';
2
+ import type { SystemLogger } from '../plugins_core/types.js';
3
+ import type { ChildProcess } from './spawn.js';
4
+ export declare const captureStandardError: (childProcess: ChildProcess, systemLog: SystemLogger, eventName: string, featureFlags: FeatureFlags) => () => void;
@@ -0,0 +1,20 @@
1
+ export const captureStandardError = (childProcess, systemLog, eventName, featureFlags) => {
2
+ if (!featureFlags.netlify_build_plugin_system_log) {
3
+ return () => {
4
+ // no-op
5
+ };
6
+ }
7
+ let receivedChunks = false;
8
+ const listener = (chunk) => {
9
+ if (!receivedChunks) {
10
+ receivedChunks = true;
11
+ systemLog(`Plugin failed to initialize during the "${eventName}" phase`);
12
+ }
13
+ systemLog(chunk.toString().trimEnd());
14
+ };
15
+ childProcess.stderr?.on('data', listener);
16
+ const cleanup = () => {
17
+ childProcess.stderr?.removeListener('data', listener);
18
+ };
19
+ return cleanup;
20
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/build",
3
- "version": "29.44.0",
3
+ "version": "29.45.0",
4
4
  "description": "Netlify build module",
5
5
  "type": "module",
6
6
  "exports": "./lib/index.js",
@@ -165,5 +165,5 @@
165
165
  "engines": {
166
166
  "node": "^14.16.0 || >=16.0.0"
167
167
  },
168
- "gitHead": "bd868b81a1ead42e337bea8bebfee0d51307e078"
168
+ "gitHead": "492b2e3de82bc00b0427eac5a12f847bb72404fa"
169
169
  }