@netlify/build 0.4.48 → 1.0.0-dl-test

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.
Files changed (243) hide show
  1. package/bin.js +5 -0
  2. package/lib/core/bin.js +66 -0
  3. package/lib/core/build.js +392 -0
  4. package/lib/core/config.js +124 -0
  5. package/lib/core/constants.js +116 -0
  6. package/lib/core/dev.js +27 -0
  7. package/lib/core/dry.js +21 -0
  8. package/lib/core/feature_flags.js +17 -0
  9. package/lib/core/flags.js +206 -0
  10. package/lib/core/lingering.js +68 -0
  11. package/lib/core/main.js +114 -0
  12. package/lib/core/missing_side_file.js +17 -0
  13. package/lib/core/normalize_flags.js +62 -0
  14. package/lib/core/report_metrics.js +17 -0
  15. package/lib/core/severity.js +21 -0
  16. package/lib/core/types.js +8 -0
  17. package/lib/core/user_node_version.js +32 -0
  18. package/lib/env/changes.js +43 -0
  19. package/lib/env/main.js +14 -0
  20. package/lib/env/metadata.js +68 -0
  21. package/lib/error/api.js +37 -0
  22. package/lib/error/build.js +36 -0
  23. package/lib/error/cancel.js +7 -0
  24. package/lib/error/colors.js +9 -0
  25. package/lib/error/handle.js +44 -0
  26. package/lib/error/info.js +37 -0
  27. package/lib/error/monitor/location.js +16 -0
  28. package/lib/error/monitor/normalize.js +86 -0
  29. package/lib/error/monitor/print.js +20 -0
  30. package/lib/error/monitor/report.js +120 -0
  31. package/lib/error/monitor/start.js +61 -0
  32. package/lib/error/parse/clean_stack.js +70 -0
  33. package/lib/error/parse/location.js +50 -0
  34. package/lib/error/parse/normalize.js +24 -0
  35. package/lib/error/parse/parse.js +67 -0
  36. package/lib/error/parse/plugin.js +55 -0
  37. package/lib/error/parse/properties.js +16 -0
  38. package/lib/error/parse/serialize_log.js +34 -0
  39. package/lib/error/parse/serialize_status.js +18 -0
  40. package/lib/error/parse/stack.js +34 -0
  41. package/lib/error/report.js +29 -0
  42. package/lib/error/type.js +177 -0
  43. package/lib/install/functions.js +20 -0
  44. package/lib/install/local.js +45 -0
  45. package/lib/install/main.js +67 -0
  46. package/lib/install/missing.js +54 -0
  47. package/lib/log/colors.js +28 -0
  48. package/lib/log/description.js +21 -0
  49. package/lib/log/header.js +12 -0
  50. package/lib/log/header_func.js +13 -0
  51. package/lib/log/logger.js +140 -0
  52. package/lib/log/messages/compatibility.js +120 -0
  53. package/lib/log/messages/config.js +91 -0
  54. package/lib/log/messages/core.js +49 -0
  55. package/lib/log/messages/core_steps.js +75 -0
  56. package/lib/log/messages/dry.js +41 -0
  57. package/lib/log/messages/install.js +25 -0
  58. package/lib/log/messages/ipc.js +29 -0
  59. package/lib/log/messages/mutations.js +62 -0
  60. package/lib/log/messages/plugins.js +25 -0
  61. package/lib/log/messages/status.js +14 -0
  62. package/lib/log/messages/steps.js +18 -0
  63. package/lib/log/serialize.js +10 -0
  64. package/lib/log/stream.js +68 -0
  65. package/lib/log/theme.js +27 -0
  66. package/lib/plugins/child/diff.js +46 -0
  67. package/lib/plugins/child/error.js +26 -0
  68. package/lib/plugins/child/lazy.js +15 -0
  69. package/lib/plugins/child/load.js +22 -0
  70. package/lib/plugins/child/logic.js +57 -0
  71. package/lib/plugins/child/main.js +37 -0
  72. package/lib/plugins/child/run.js +19 -0
  73. package/lib/plugins/child/status.js +63 -0
  74. package/lib/plugins/child/typescript.js +28 -0
  75. package/lib/plugins/child/utils.js +42 -0
  76. package/lib/plugins/child/validate.js +31 -0
  77. package/lib/plugins/compatibility.js +104 -0
  78. package/lib/plugins/error.js +46 -0
  79. package/lib/plugins/events.js +12 -0
  80. package/lib/plugins/expected_version.js +81 -0
  81. package/lib/plugins/internal.js +10 -0
  82. package/lib/plugins/ipc.js +120 -0
  83. package/lib/plugins/list.js +73 -0
  84. package/lib/plugins/load.js +50 -0
  85. package/lib/plugins/manifest/check.js +85 -0
  86. package/lib/plugins/manifest/load.js +38 -0
  87. package/lib/plugins/manifest/main.js +19 -0
  88. package/lib/plugins/manifest/path.js +24 -0
  89. package/lib/plugins/manifest/validate.js +91 -0
  90. package/lib/plugins/node_version.js +35 -0
  91. package/lib/plugins/options.js +70 -0
  92. package/lib/plugins/pinned_version.js +83 -0
  93. package/lib/plugins/resolve.js +110 -0
  94. package/lib/plugins/spawn.js +55 -0
  95. package/lib/plugins_core/add.js +35 -0
  96. package/lib/plugins_core/build_command.js +50 -0
  97. package/lib/plugins_core/deploy/buildbot_client.js +87 -0
  98. package/lib/plugins_core/deploy/index.js +49 -0
  99. package/lib/plugins_core/deploy/manifest.yml +1 -0
  100. package/lib/plugins_core/edge_functions/index.js +106 -0
  101. package/lib/plugins_core/edge_functions/lib/error.js +17 -0
  102. package/lib/plugins_core/edge_functions/validate_manifest/validate_edge_functions_manifest.js +14 -0
  103. package/lib/plugins_core/functions/error.js +123 -0
  104. package/lib/plugins_core/functions/feature_flags.js +5 -0
  105. package/lib/plugins_core/functions/index.js +137 -0
  106. package/lib/plugins_core/functions/utils.js +45 -0
  107. package/lib/plugins_core/functions/zisi.js +64 -0
  108. package/lib/plugins_core/functions_install/index.js +10 -0
  109. package/lib/plugins_core/list.js +20 -0
  110. package/lib/plugins_core/save_artifacts/index.js +33 -0
  111. package/lib/report/statsd.js +56 -0
  112. package/lib/status/add.js +30 -0
  113. package/lib/status/colors.js +18 -0
  114. package/lib/status/load_error.js +10 -0
  115. package/lib/status/report.js +83 -0
  116. package/lib/status/success.js +14 -0
  117. package/lib/steps/core_step.js +62 -0
  118. package/lib/steps/error.js +65 -0
  119. package/lib/steps/get.js +44 -0
  120. package/lib/steps/plugin.js +55 -0
  121. package/lib/steps/return.js +27 -0
  122. package/lib/steps/run_core_steps.js +117 -0
  123. package/lib/steps/run_step.js +200 -0
  124. package/lib/steps/run_steps.js +102 -0
  125. package/lib/steps/update_config.js +66 -0
  126. package/lib/telemetry/main.js +94 -0
  127. package/lib/time/aggregate.js +109 -0
  128. package/lib/time/main.js +31 -0
  129. package/lib/time/measure.js +16 -0
  130. package/lib/time/report.js +30 -0
  131. package/lib/utils/errors.js +13 -0
  132. package/lib/utils/json.js +10 -0
  133. package/lib/utils/omit.js +3 -0
  134. package/lib/utils/package.js +24 -0
  135. package/lib/utils/remove_falsy.js +8 -0
  136. package/lib/utils/resolve.js +41 -0
  137. package/lib/utils/runtime.js +5 -0
  138. package/lib/utils/semver.js +28 -0
  139. package/package.json +119 -56
  140. package/types/config/build.d.ts +52 -0
  141. package/types/config/functions.d.ts +36 -0
  142. package/types/config/inputs.d.ts +7 -0
  143. package/types/config/netlify_config.d.ts +58 -0
  144. package/types/index.d.ts +7 -0
  145. package/types/netlify_event_handler.d.ts +29 -0
  146. package/types/netlify_plugin.d.ts +29 -0
  147. package/types/netlify_plugin_constants.d.ts +51 -0
  148. package/types/netlify_plugin_options.d.ts +23 -0
  149. package/types/options/index.d.ts +1 -0
  150. package/types/options/netlify_plugin_build_util.d.ts +7 -0
  151. package/types/options/netlify_plugin_cache_util.d.ts +39 -0
  152. package/types/options/netlify_plugin_functions_util.d.ts +32 -0
  153. package/types/options/netlify_plugin_git_util.d.ts +41 -0
  154. package/types/options/netlify_plugin_run_util.d.ts +24 -0
  155. package/types/options/netlify_plugin_status_util.d.ts +24 -0
  156. package/types/options/netlify_plugin_utils.d.ts +15 -0
  157. package/types/utils/json_value.d.ts +1 -0
  158. package/types/utils/many.d.ts +6 -0
  159. package/README.md +0 -3
  160. package/src/core/bin.js +0 -139
  161. package/src/core/commands.js +0 -304
  162. package/src/core/config.js +0 -130
  163. package/src/core/constants.js +0 -88
  164. package/src/core/dry.js +0 -23
  165. package/src/core/main.js +0 -196
  166. package/src/env/changes.js +0 -49
  167. package/src/env/git.js +0 -27
  168. package/src/env/main.js +0 -97
  169. package/src/env/metadata.js +0 -81
  170. package/src/error/api.js +0 -32
  171. package/src/error/build.js +0 -32
  172. package/src/error/cancel.js +0 -22
  173. package/src/error/colors.js +0 -13
  174. package/src/error/info.js +0 -12
  175. package/src/error/monitor/normalize.js +0 -50
  176. package/src/error/monitor/print.js +0 -43
  177. package/src/error/monitor/report.js +0 -140
  178. package/src/error/monitor/start.js +0 -50
  179. package/src/error/parse/clean_stack.js +0 -86
  180. package/src/error/parse/location.js +0 -50
  181. package/src/error/parse/parse.js +0 -87
  182. package/src/error/parse/plugin.js +0 -68
  183. package/src/error/parse/properties.js +0 -20
  184. package/src/error/parse/serialize_log.js +0 -46
  185. package/src/error/parse/serialize_status.js +0 -28
  186. package/src/error/parse/stack.js +0 -45
  187. package/src/error/process.js +0 -13
  188. package/src/error/type.js +0 -143
  189. package/src/install/functions.js +0 -52
  190. package/src/install/local.js +0 -65
  191. package/src/install/main.js +0 -103
  192. package/src/install/missing.js +0 -86
  193. package/src/log/colors.js +0 -59
  194. package/src/log/description.js +0 -38
  195. package/src/log/header.js +0 -19
  196. package/src/log/logger.js +0 -55
  197. package/src/log/main.js +0 -348
  198. package/src/log/old_version.js +0 -45
  199. package/src/log/serialize.js +0 -15
  200. package/src/log/stream.js +0 -15
  201. package/src/log/theme.js +0 -32
  202. package/src/log/timer.js +0 -28
  203. package/src/plugins/child/api.js +0 -59
  204. package/src/plugins/child/error.js +0 -39
  205. package/src/plugins/child/load.js +0 -40
  206. package/src/plugins/child/logic.js +0 -31
  207. package/src/plugins/child/main.js +0 -48
  208. package/src/plugins/child/normalize.js +0 -20
  209. package/src/plugins/child/run.js +0 -34
  210. package/src/plugins/child/status.js +0 -53
  211. package/src/plugins/child/utils.js +0 -43
  212. package/src/plugins/child/validate.js +0 -46
  213. package/src/plugins/error.js +0 -44
  214. package/src/plugins/events.js +0 -77
  215. package/src/plugins/ipc.js +0 -136
  216. package/src/plugins/load.js +0 -66
  217. package/src/plugins/manifest/check.js +0 -80
  218. package/src/plugins/manifest/load.js +0 -47
  219. package/src/plugins/manifest/main.js +0 -28
  220. package/src/plugins/manifest/path.js +0 -12
  221. package/src/plugins/manifest/validate.js +0 -136
  222. package/src/plugins/node_version.js +0 -74
  223. package/src/plugins/options.js +0 -78
  224. package/src/plugins/package.js +0 -17
  225. package/src/plugins/resolve.js +0 -159
  226. package/src/plugins/spawn.js +0 -82
  227. package/src/plugins_core/cache/manifest.yml +0 -1
  228. package/src/plugins_core/cache/plugin.js +0 -75
  229. package/src/plugins_core/functions/manifest.yml +0 -1
  230. package/src/plugins_core/functions/plugin.js +0 -37
  231. package/src/plugins_core/functions_install/plugin.js +0 -14
  232. package/src/plugins_core/main.js +0 -38
  233. package/src/status/add.js +0 -56
  234. package/src/status/colors.js +0 -25
  235. package/src/status/report.js +0 -57
  236. package/src/telemetry/complete.js +0 -49
  237. package/src/telemetry/request.js +0 -28
  238. package/src/telemetry/track.js +0 -33
  239. package/src/utils/omit.js +0 -8
  240. package/src/utils/polyfills.js +0 -3
  241. package/src/utils/remove_falsy.js +0 -12
  242. package/src/utils/resolve.js +0 -39
  243. /package/{src → lib}/plugins_core/functions_install/manifest.yml +0 -0
@@ -0,0 +1,45 @@
1
+ import { packageDirectory } from 'pkg-dir';
2
+ import { logInstallLocalPluginsDeps } from '../log/messages/install.js';
3
+ import { installDependencies } from './main.js';
4
+ // Install dependencies of local plugins.
5
+ // Users must add this plugin to their `netlify.toml` `plugins` to use this
6
+ // feature. We don't want to provide it by default because this makes build
7
+ // slow and buggy.
8
+ export const installLocalPluginsDependencies = async function ({ plugins, pluginsOptions, buildDir, mode, logs }) {
9
+ if (!plugins.some(isLocalInstallOptIn)) {
10
+ return;
11
+ }
12
+ const localPluginsOptions = getLocalPluginsOptions(pluginsOptions);
13
+ if (localPluginsOptions.length === 0) {
14
+ return;
15
+ }
16
+ const localPluginsOptionsA = await removeMainRoot(localPluginsOptions, buildDir);
17
+ if (localPluginsOptionsA.length === 0) {
18
+ return;
19
+ }
20
+ logInstallLocalPluginsDeps(logs, localPluginsOptionsA);
21
+ await Promise.all(localPluginsOptionsA.map(({ packageDir }) => installDependencies({ packageRoot: packageDir, isLocal: mode !== 'buildbot' })));
22
+ };
23
+ const isLocalInstallOptIn = function (plugin) {
24
+ return plugin.package === LOCAL_INSTALL_PLUGIN_NAME;
25
+ };
26
+ export const LOCAL_INSTALL_PLUGIN_NAME = '@netlify/plugin-local-install-core';
27
+ // Core plugins and non-local plugins already have their dependencies installed
28
+ const getLocalPluginsOptions = function (pluginsOptions) {
29
+ return pluginsOptions.filter(isLocalPlugin).filter(isUnique).filter(hasPackageDir);
30
+ };
31
+ const isLocalPlugin = function ({ loadedFrom }) {
32
+ return loadedFrom === 'local';
33
+ };
34
+ // Remove duplicates
35
+ const isUnique = function ({ packageDir }, index, pluginsOptions) {
36
+ return pluginsOptions.slice(index + 1).every((pluginOption) => pluginOption.packageDir !== packageDir);
37
+ };
38
+ const hasPackageDir = function ({ packageDir }) {
39
+ return packageDir !== undefined;
40
+ };
41
+ // We only install dependencies of local plugins that have their own `package.json`
42
+ const removeMainRoot = async function (localPluginsOptions, buildDir) {
43
+ const mainPackageDir = await packageDirectory({ cwd: buildDir });
44
+ return localPluginsOptions.filter(({ packageDir }) => packageDir !== mainPackageDir);
45
+ };
@@ -0,0 +1,67 @@
1
+ import { homedir } from 'os';
2
+ import { execa } from 'execa';
3
+ import { pathExists } from 'path-exists';
4
+ import { addErrorInfo } from '../error/info.js';
5
+ // Install Node.js dependencies in a specific directory
6
+ export const installDependencies = function ({ packageRoot, isLocal }) {
7
+ return runCommand({ packageRoot, isLocal, type: 'install' });
8
+ };
9
+ // Add new Node.js dependencies, with exact semver ranges
10
+ export const addExactDependencies = function ({ packageRoot, isLocal, packages }) {
11
+ return runCommand({ packageRoot, packages, isLocal, type: 'addExact' });
12
+ };
13
+ const runCommand = async function ({ packageRoot, packages = [], isLocal, type }) {
14
+ try {
15
+ const [command, ...args] = await getCommand({ packageRoot, type, isLocal });
16
+ await execa(command, [...args, ...packages], { cwd: packageRoot, all: true });
17
+ }
18
+ catch (error) {
19
+ const message = getErrorMessage(error.all);
20
+ const errorA = new Error(`Error while installing dependencies in ${packageRoot}\n${message}`);
21
+ addErrorInfo(errorA, { type: 'dependencies' });
22
+ throw errorA;
23
+ }
24
+ };
25
+ // Retrieve the shell command to install or add dependencies
26
+ const getCommand = async function ({ packageRoot, type, isLocal }) {
27
+ const manager = await getManager(type, packageRoot);
28
+ const command = COMMANDS[manager][type];
29
+ const commandA = addYarnCustomCache(command, manager, isLocal);
30
+ return commandA;
31
+ };
32
+ const getManager = async function (type, packageRoot) {
33
+ // `addDependencies()` always uses npm
34
+ if (type === 'addExact') {
35
+ return 'npm';
36
+ }
37
+ if (await pathExists(`${packageRoot}/yarn.lock`)) {
38
+ return 'yarn';
39
+ }
40
+ return 'npm';
41
+ };
42
+ const COMMANDS = {
43
+ npm: {
44
+ addExact: ['npm', 'install', '--no-progress', '--no-audit', '--no-fund', '--save-exact'],
45
+ install: ['npm', 'install', '--no-progress', '--no-audit', '--no-fund'],
46
+ },
47
+ yarn: {
48
+ install: ['yarn', 'install', '--no-progress', '--non-interactive'],
49
+ },
50
+ };
51
+ // In CI, yarn uses a custom cache folder
52
+ const addYarnCustomCache = function (command, manager, isLocal) {
53
+ if (manager !== 'yarn' || isLocal) {
54
+ return command;
55
+ }
56
+ return [...command, '--cache-folder', YARN_CI_CACHE_DIR];
57
+ };
58
+ const YARN_CI_CACHE_DIR = `${homedir()}/.yarn_cache`;
59
+ // Retrieve message to add to install errors
60
+ const getErrorMessage = function (allOutput) {
61
+ return allOutput.split('\n').filter(isNotNpmLogMessage).join('\n');
62
+ };
63
+ // Debug logs shown at the end of npm errors is not useful in Netlify Build
64
+ const isNotNpmLogMessage = function (line) {
65
+ return NPM_LOG_MESSAGES.every((message) => !line.includes(message));
66
+ };
67
+ const NPM_LOG_MESSAGES = ['complete log of this run', '-debug.log'];
@@ -0,0 +1,54 @@
1
+ import { promises as fs } from 'fs';
2
+ import { normalize } from 'path';
3
+ import { pathExists } from 'path-exists';
4
+ import { isFile } from 'path-type';
5
+ import { logInstallMissingPlugins } from '../log/messages/install.js';
6
+ import { addExactDependencies } from './main.js';
7
+ // Automatically install plugins if not already installed.
8
+ // Since this is done under the hood, we always use `npm` with specific `npm`
9
+ // options. We do not allow configure the package manager nor its options.
10
+ // Users requiring `yarn` or custom npm/yarn flags should install the plugin in
11
+ // their `package.json`.
12
+ export const installMissingPlugins = async function ({ missingPlugins, autoPluginsDir, mode, logs }) {
13
+ const packages = missingPlugins.map(getPackage);
14
+ logInstallMissingPlugins(logs, packages);
15
+ await createAutoPluginsDir(logs, autoPluginsDir);
16
+ await addExactDependencies({ packageRoot: autoPluginsDir, isLocal: mode !== 'buildbot', packages });
17
+ };
18
+ // We pin the version without using semver ranges ^ nor ~
19
+ const getPackage = function ({ packageName, expectedVersion }) {
20
+ return `${packageName}@${expectedVersion}`;
21
+ };
22
+ const createAutoPluginsDir = async function (logs, autoPluginsDir) {
23
+ await ensureDir(logs, autoPluginsDir);
24
+ await createPackageJson(autoPluginsDir);
25
+ };
26
+ // Create the directory if it does not exist
27
+ const ensureDir = async function (logs, autoPluginsDir) {
28
+ if (await pathExists(autoPluginsDir)) {
29
+ return;
30
+ }
31
+ // If `.netlify` exists but is not a directory, we remove it first
32
+ const autoPluginsParent = normalize(`${autoPluginsDir}/..`);
33
+ if (await isFile(autoPluginsParent)) {
34
+ await fs.unlink(autoPluginsParent);
35
+ }
36
+ await fs.mkdir(autoPluginsDir, { recursive: true });
37
+ };
38
+ // Create a dummy `package.json` so we can run `npm install` and get a lock file
39
+ const createPackageJson = async function (autoPluginsDir) {
40
+ const packageJsonPath = `${autoPluginsDir}/package.json`;
41
+ if (await pathExists(packageJsonPath)) {
42
+ return;
43
+ }
44
+ const packageJsonContent = JSON.stringify(AUTO_PLUGINS_PACKAGE_JSON, null, 2);
45
+ await fs.writeFile(packageJsonPath, packageJsonContent);
46
+ };
47
+ const AUTO_PLUGINS_PACKAGE_JSON = {
48
+ name: 'netlify-local-plugins',
49
+ description: 'This directory contains Build plugins that have been automatically installed by Netlify.',
50
+ version: '1.0.0',
51
+ private: true,
52
+ author: 'Netlify',
53
+ license: 'MIT',
54
+ };
@@ -0,0 +1,28 @@
1
+ import { inspect } from 'util';
2
+ import supportsColor from 'supports-color';
3
+ const hasColors = () => supportsColor.stdout !== false;
4
+ /**
5
+ * Plugin child processes use `stdio: 'pipe'` so they are always
6
+ * non-interactive even if the parent is an interactive TTY. This means they
7
+ * would normally lose colors. If the parent has colors, we pass an environment
8
+ * variable to the child process to force colors.
9
+ */
10
+ export const getParentColorEnv = function () {
11
+ if (!hasColors()) {
12
+ return {};
13
+ }
14
+ return { FORCE_COLOR: '1' };
15
+ };
16
+ /**
17
+ * Child processes and the buildbot relies on `FORCE_COLOR=1` to set colors.
18
+ * However `utils.inspect()` (used by `console.log()`) uses
19
+ * `process.stdout.hasColors` which is always `undefined` when the TTY is
20
+ * non-interactive. So we need to set `util.inspect.defaultOptions.colors`
21
+ * manually both in plugin processes.
22
+ */
23
+ export const setInspectColors = function () {
24
+ if (!hasColors()) {
25
+ return;
26
+ }
27
+ inspect.defaultOptions.colors = true;
28
+ };
@@ -0,0 +1,21 @@
1
+ export const getBuildCommandDescription = function (buildCommandOrigin) {
2
+ return BUILD_COMMAND_DESCRIPTIONS[buildCommandOrigin];
3
+ };
4
+ const BUILD_COMMAND_DESCRIPTIONS = {
5
+ ui: 'Build command from Netlify app',
6
+ config: 'build.command from netlify.toml',
7
+ inline: 'build.command from a plugin',
8
+ };
9
+ /** Retrieve human-friendly plugin origin */
10
+ export const getPluginOrigin = function (loadedFrom, origin) {
11
+ const originName = PLUGIN_ORIGINS[origin];
12
+ if (loadedFrom === 'package.json') {
13
+ return `from ${originName} and package.json`;
14
+ }
15
+ return `from ${originName}`;
16
+ };
17
+ const PLUGIN_ORIGINS = {
18
+ core: 'core',
19
+ ui: 'Netlify app',
20
+ config: 'netlify.toml',
21
+ };
@@ -0,0 +1,12 @@
1
+ import stringWidth from 'string-width';
2
+ const HEADER_MIN_WIDTH = 60;
3
+ const PADDING_WIDTH = 2;
4
+ /** Print a rectangular header */
5
+ export const getHeader = function (message) {
6
+ const messageWidth = stringWidth(message);
7
+ const headerWidth = Math.max(HEADER_MIN_WIDTH, messageWidth);
8
+ const line = '─'.repeat(headerWidth + PADDING_WIDTH * 2);
9
+ const paddingRight = ' '.repeat(PADDING_WIDTH + headerWidth - messageWidth);
10
+ return `${message}${paddingRight}
11
+ ${line}`;
12
+ };
@@ -0,0 +1,13 @@
1
+ import { parseErrorInfo } from '../error/parse/parse.js';
2
+ import { logHeader, logErrorHeader } from './logger.js';
3
+ /** Retrieve successful or error header depending on whether `error` exists */
4
+ export const getLogHeaderFunc = function (error) {
5
+ if (error === undefined) {
6
+ return logHeader;
7
+ }
8
+ const { severity } = parseErrorInfo(error);
9
+ if (severity === 'none') {
10
+ return logHeader;
11
+ }
12
+ return logErrorHeader;
13
+ };
@@ -0,0 +1,140 @@
1
+ import { createWriteStream } from 'fs';
2
+ import figures from 'figures';
3
+ import indentString from 'indent-string';
4
+ import { getHeader } from './header.js';
5
+ import { serializeArray, serializeObject } from './serialize.js';
6
+ import { THEME } from './theme.js';
7
+ const INDENT_SIZE = 2;
8
+ /**
9
+ * We need to add a zero width space character in empty lines. Otherwise the
10
+ * buildbot removes those due to a bug: https://github.com/netlify/buildbot/issues/595
11
+ */
12
+ const EMPTY_LINES_REGEXP = /^\s*$/gm;
13
+ const EMPTY_LINE = '\u{200B}';
14
+ /**
15
+ * When the `buffer` option is true, we return logs instead of printing them
16
+ * on the console. The logs are accumulated in a `logs` array variable.
17
+ */
18
+ export const getBufferLogs = (config) => {
19
+ const { buffer = false } = config;
20
+ if (!buffer) {
21
+ return;
22
+ }
23
+ return { stdout: [], stderr: [] };
24
+ };
25
+ // Core logging utility, used by the other methods.
26
+ // This should be used instead of `console.log()` as it allows us to instrument
27
+ // how any build logs is being printed.
28
+ export const log = function (logs, string, config = {}) {
29
+ const { indent = false, color } = config;
30
+ const stringA = indent ? indentString(string, INDENT_SIZE) : string;
31
+ const stringB = String(stringA).replace(EMPTY_LINES_REGEXP, EMPTY_LINE);
32
+ const stringC = color === undefined ? stringB : color(stringB);
33
+ if (logs !== undefined) {
34
+ // `logs` is a stateful variable
35
+ logs.stdout.push(stringC);
36
+ return;
37
+ }
38
+ console.log(stringC);
39
+ };
40
+ const serializeIndentedArray = function (array) {
41
+ return serializeArray(array.map(serializeIndentedItem));
42
+ };
43
+ const serializeIndentedItem = function (item) {
44
+ return indentString(item, INDENT_SIZE + 1).trimStart();
45
+ };
46
+ export const logError = function (logs, string, opts = {}) {
47
+ log(logs, string, { color: THEME.errorLine, ...opts });
48
+ };
49
+ export const logWarning = function (logs, string, opts = {}) {
50
+ log(logs, string, { color: THEME.warningLine, ...opts });
51
+ };
52
+ // Print a message that is under a header/subheader, i.e. indented
53
+ export const logMessage = function (logs, string, opts = {}) {
54
+ log(logs, string, { indent: true, ...opts });
55
+ };
56
+ // Print an object
57
+ export const logObject = function (logs, object, opts) {
58
+ logMessage(logs, serializeObject(object), opts);
59
+ };
60
+ // Print an array
61
+ export const logArray = function (logs, array, opts = {}) {
62
+ logMessage(logs, serializeIndentedArray(array), { color: THEME.none, ...opts });
63
+ };
64
+ // Print an array of errors
65
+ export const logErrorArray = function (logs, array, opts = {}) {
66
+ logMessage(logs, serializeIndentedArray(array), { color: THEME.errorLine, ...opts });
67
+ };
68
+ // Print an array of warnings
69
+ export const logWarningArray = function (logs, array, opts = {}) {
70
+ logMessage(logs, serializeIndentedArray(array), { color: THEME.warningLine, ...opts });
71
+ };
72
+ // Print a main section header
73
+ export const logHeader = function (logs, string, opts = {}) {
74
+ log(logs, `\n${getHeader(string)}`, { color: THEME.header, ...opts });
75
+ };
76
+ // Print a main section header, when an error happened
77
+ export const logErrorHeader = function (logs, string, opts = {}) {
78
+ logHeader(logs, string, { color: THEME.errorHeader, ...opts });
79
+ };
80
+ // Print a sub-section header
81
+ export const logSubHeader = function (logs, string, opts = {}) {
82
+ log(logs, `\n${figures.pointer} ${string}`, { color: THEME.subHeader, ...opts });
83
+ };
84
+ // Print a sub-section header, when an error happened
85
+ export const logErrorSubHeader = function (logs, string, opts = {}) {
86
+ logSubHeader(logs, string, { color: THEME.errorSubHeader, ...opts });
87
+ };
88
+ // Print a sub-section header, when a warning happened
89
+ export const logWarningSubHeader = function (logs, string, opts = {}) {
90
+ logSubHeader(logs, string, { color: THEME.warningSubHeader, ...opts });
91
+ };
92
+ // Combines an array of elements into a single string, separated by a space,
93
+ // and with basic serialization of non-string types
94
+ const reduceLogLines = function (lines) {
95
+ return lines
96
+ .map((input) => {
97
+ if (input instanceof Error) {
98
+ return `${input.message} ${input.stack}`;
99
+ }
100
+ if (typeof input === 'object') {
101
+ try {
102
+ return JSON.stringify(input);
103
+ }
104
+ catch {
105
+ // Value could not be serialized to JSON, so we return the string
106
+ // representation.
107
+ return String(input);
108
+ }
109
+ }
110
+ return String(input);
111
+ })
112
+ .join(' ');
113
+ };
114
+ /**
115
+ * Builds a function for logging data to the system logger (i.e. hidden from
116
+ * the user-facing build logs)
117
+ */
118
+ export const getSystemLogger = function (logs, debug,
119
+ /** A system log file descriptor, if non is provided it will be a noop logger */
120
+ systemLogFile) {
121
+ // If the `debug` flag is used, we return a function that pipes system logs
122
+ // to the regular logger, as the intention is for them to end up in stdout.
123
+ if (debug) {
124
+ return (...args) => log(logs, reduceLogLines(args));
125
+ }
126
+ // If there's not a file descriptor configured for system logs and `debug`
127
+ // is not set, we return a no-op function that will swallow the errors.
128
+ if (!systemLogFile) {
129
+ return () => {
130
+ // no-op
131
+ };
132
+ }
133
+ // Return a function that writes to the file descriptor configured for system
134
+ // logs.
135
+ const fileDescriptor = createWriteStream('', { fd: systemLogFile });
136
+ fileDescriptor.on('error', () => {
137
+ logError(logs, 'Could not write to system log file');
138
+ });
139
+ return (...args) => fileDescriptor.write(`${reduceLogLines(args)}\n`);
140
+ };
@@ -0,0 +1,120 @@
1
+ import semver from 'semver';
2
+ import { isRuntime } from '../../utils/runtime.js';
3
+ import { isPreviousMajor } from '../../utils/semver.js';
4
+ import { getPluginOrigin } from '../description.js';
5
+ import { logArray, logSubHeader, logWarningArray, logWarningSubHeader } from '../logger.js';
6
+ import { THEME } from '../theme.js';
7
+ export const logRuntime = (logs, pluginOptions) => {
8
+ const runtimes = pluginOptions.filter(isRuntime);
9
+ // Once we have more runtimes, this hardcoded check should be removed
10
+ if (runtimes.length !== 0) {
11
+ const [nextRuntime] = runtimes;
12
+ logSubHeader(logs, `Using Next.js Runtime - v${nextRuntime.pluginPackageJson.version}`);
13
+ }
14
+ };
15
+ export const logLoadingPlugins = function (logs, pluginsOptions, debug) {
16
+ const loadingPlugins = pluginsOptions
17
+ .filter(isNotCorePlugin)
18
+ // We don't want to show runtimes as plugins
19
+ .filter((plugin) => !isRuntime(plugin))
20
+ .map((pluginOptions) => getPluginDescription(pluginOptions, debug));
21
+ if (loadingPlugins.length === 0) {
22
+ return;
23
+ }
24
+ logSubHeader(logs, 'Loading plugins');
25
+ logArray(logs, loadingPlugins);
26
+ };
27
+ // We only logs plugins explicitly enabled by users
28
+ const isNotCorePlugin = function ({ origin }) {
29
+ return origin !== 'core';
30
+ };
31
+ const getPluginDescription = function ({ packageName, pluginPackageJson: { version }, loadedFrom, origin, pinnedVersion, latestVersion, expectedVersion, compatibleVersion, }, debug) {
32
+ const versionedPackage = getVersionedPackage(version);
33
+ const pluginOrigin = getPluginOrigin(loadedFrom, origin);
34
+ const description = `${THEME.highlightWords(packageName)}${versionedPackage} ${pluginOrigin}`;
35
+ if (!debug) {
36
+ return description;
37
+ }
38
+ const versions = Object.entries({
39
+ pinned: pinnedVersion,
40
+ latest: latestVersion,
41
+ expected: expectedVersion,
42
+ compatible: compatibleVersion,
43
+ })
44
+ .filter(hasVersion)
45
+ .map(getVersionField);
46
+ if (versions.length === 0) {
47
+ return description;
48
+ }
49
+ return `${description} (${versions.join(', ')})`;
50
+ };
51
+ const hasVersion = function ([, version]) {
52
+ return version !== undefined;
53
+ };
54
+ const getVersionField = function ([versionFieldName, version]) {
55
+ return `${versionFieldName} ${version}`;
56
+ };
57
+ // Print a warning message when old versions plugins are used.
58
+ // This can only happen when they are installed to `package.json`.
59
+ export const logOutdatedPlugins = function (logs, pluginsOptions) {
60
+ const outdatedPlugins = pluginsOptions.filter(hasOutdatedVersion).map(getOutdatedPlugin);
61
+ if (outdatedPlugins.length === 0) {
62
+ return;
63
+ }
64
+ logWarningSubHeader(logs, 'Outdated plugins');
65
+ logWarningArray(logs, outdatedPlugins);
66
+ };
67
+ const hasOutdatedVersion = function ({ pluginPackageJson: { version }, latestVersion }) {
68
+ return version !== undefined && latestVersion !== undefined && semver.lt(version, latestVersion);
69
+ };
70
+ const getOutdatedPlugin = function ({ packageName, pluginPackageJson: { version }, latestVersion, migrationGuide, loadedFrom, origin, }) {
71
+ const versionedPackage = getVersionedPackage(version);
72
+ const outdatedDescription = getOutdatedDescription({ latestVersion, migrationGuide, loadedFrom, origin });
73
+ return `${THEME.warningHighlightWords(packageName)}${versionedPackage}: ${outdatedDescription}`;
74
+ };
75
+ const getOutdatedDescription = function ({ latestVersion, migrationGuide, loadedFrom, origin }) {
76
+ const upgradeInstruction = getUpgradeInstruction(loadedFrom, origin);
77
+ if (migrationGuide === undefined) {
78
+ return `latest version is ${latestVersion}\n${upgradeInstruction}`;
79
+ }
80
+ return `latest version is ${latestVersion}\nMigration guide: ${migrationGuide}\n${upgradeInstruction}`;
81
+ };
82
+ const getUpgradeInstruction = function (loadedFrom, origin) {
83
+ if (loadedFrom === 'package.json') {
84
+ return 'To upgrade this plugin, please update its version in "package.json"';
85
+ }
86
+ if (origin === 'ui') {
87
+ return 'To upgrade this plugin, please uninstall and re-install it from the Netlify plugins directory (https://app.netlify.com/plugins)';
88
+ }
89
+ return 'To upgrade this plugin, please remove it from "netlify.toml" and install it from the Netlify plugins directory instead (https://app.netlify.com/plugins)';
90
+ };
91
+ // Print a warning message when plugins are using a version that is too recent
92
+ // and does not meet some `compatibility` expectations.
93
+ // This can only happen when they are installed to `package.json`.
94
+ export const logIncompatiblePlugins = function (logs, pluginsOptions) {
95
+ const incompatiblePlugins = pluginsOptions.filter(hasIncompatibleVersion).map(getIncompatiblePlugin);
96
+ if (incompatiblePlugins.length === 0) {
97
+ return;
98
+ }
99
+ logWarningSubHeader(logs, 'Incompatible plugins');
100
+ logWarningArray(logs, incompatiblePlugins);
101
+ };
102
+ const hasIncompatibleVersion = function ({ pluginPackageJson: { version }, compatibleVersion, compatWarning }) {
103
+ return (compatWarning !== '' &&
104
+ version !== undefined &&
105
+ compatibleVersion !== undefined &&
106
+ // Using only the major version prevents printing this warning message when
107
+ // a site is using the right `compatibility` version, but is using the most
108
+ // recent version due to the time gap between `npm publish` and the
109
+ // `plugins.json` update
110
+ isPreviousMajor(compatibleVersion, version));
111
+ };
112
+ const getIncompatiblePlugin = function ({ packageName, pluginPackageJson: { version }, compatibleVersion, compatWarning, }) {
113
+ const versionedPackage = getVersionedPackage(version);
114
+ return `${THEME.warningHighlightWords(packageName)}${versionedPackage}: version ${compatibleVersion} is the most recent version compatible with ${compatWarning}`;
115
+ };
116
+ // Make sure we handle `package.json` with `version` being either `undefined`
117
+ // or an empty string
118
+ const getVersionedPackage = function (version = '') {
119
+ return version === '' ? '' : `@${version}`;
120
+ };
@@ -0,0 +1,91 @@
1
+ import { cleanupConfig } from '@netlify/config';
2
+ import { DEFAULT_FEATURE_FLAGS } from '../../core/feature_flags.js';
3
+ import { omit } from '../../utils/omit.js';
4
+ import { logMessage, logObject, logSubHeader } from '../logger.js';
5
+ import { THEME } from '../theme.js';
6
+ export const logFlags = function (logs, flags, { debug }) {
7
+ const flagsA = cleanFeatureFlags(flags);
8
+ const hiddenFlags = debug ? HIDDEN_DEBUG_FLAGS : HIDDEN_FLAGS;
9
+ const flagsB = omit(flagsA, hiddenFlags);
10
+ logSubHeader(logs, 'Flags');
11
+ logObject(logs, flagsB);
12
+ };
13
+ // We only show feature flags related to `@netlify/build`.
14
+ // Also, we only print enabled feature flags.
15
+ const cleanFeatureFlags = function ({ featureFlags = {}, ...flags }) {
16
+ const cleanedFeatureFlags = Object.entries(featureFlags)
17
+ .filter(shouldPrintFeatureFlag)
18
+ .map(([featureFlagName]) => featureFlagName);
19
+ return cleanedFeatureFlags.length === 0 ? flags : { ...flags, featureFlags: cleanedFeatureFlags };
20
+ };
21
+ const shouldPrintFeatureFlag = function ([featureFlagName, enabled]) {
22
+ return enabled && featureFlagName in DEFAULT_FEATURE_FLAGS;
23
+ };
24
+ // Hidden because the value is security-sensitive
25
+ const SECURE_FLAGS = ['token', 'bugsnagKey', 'env', 'cachedConfig', 'defaultConfig'];
26
+ // Hidden because those are used in tests
27
+ const TEST_FLAGS = ['buffer', 'telemetry'];
28
+ // Hidden because those are only used internally
29
+ const INTERNAL_FLAGS = [
30
+ 'nodePath',
31
+ 'functionsDistDir',
32
+ 'edgeFunctionsDistDir',
33
+ 'defaultConfig',
34
+ 'cachedConfigPath',
35
+ 'sendStatus',
36
+ 'saveConfig',
37
+ 'statsd',
38
+ 'framework',
39
+ 'featureFlags',
40
+ 'buildbotServerSocket',
41
+ 'testOpts',
42
+ 'siteId',
43
+ 'context',
44
+ 'branch',
45
+ 'cwd',
46
+ 'repositoryRoot',
47
+ 'mode',
48
+ 'apiHost',
49
+ 'cacheDir',
50
+ 'systemLogFile',
51
+ 'timeline',
52
+ ];
53
+ const HIDDEN_FLAGS = [...SECURE_FLAGS, ...TEST_FLAGS, ...INTERNAL_FLAGS];
54
+ const HIDDEN_DEBUG_FLAGS = [...SECURE_FLAGS, ...TEST_FLAGS];
55
+ export const logBuildDir = function (logs, buildDir) {
56
+ logSubHeader(logs, 'Current directory');
57
+ logMessage(logs, buildDir);
58
+ };
59
+ export const logConfigPath = function (logs, configPath = NO_CONFIG_MESSAGE) {
60
+ logSubHeader(logs, 'Config file');
61
+ logMessage(logs, configPath);
62
+ };
63
+ const NO_CONFIG_MESSAGE = 'No config file was defined: using default values.';
64
+ export const logConfig = function ({ logs, netlifyConfig, debug }) {
65
+ if (!debug) {
66
+ return;
67
+ }
68
+ logSubHeader(logs, 'Resolved config');
69
+ logObject(logs, cleanupConfig(netlifyConfig));
70
+ };
71
+ export const logConfigOnUpdate = function ({ logs, netlifyConfig, debug }) {
72
+ if (!debug) {
73
+ return;
74
+ }
75
+ logSubHeader(logs, 'Updated config');
76
+ logObject(logs, cleanupConfig(netlifyConfig));
77
+ };
78
+ export const logConfigOnError = function ({ logs, netlifyConfig, severity }) {
79
+ if (netlifyConfig === undefined || severity === 'none') {
80
+ return;
81
+ }
82
+ logMessage(logs, THEME.errorSubHeader('Resolved config'));
83
+ logObject(logs, cleanupConfig(netlifyConfig));
84
+ };
85
+ export const logContext = function (logs, context) {
86
+ if (context === undefined) {
87
+ return;
88
+ }
89
+ logSubHeader(logs, 'Context');
90
+ logMessage(logs, context);
91
+ };
@@ -0,0 +1,49 @@
1
+ import ansiEscapes from 'ansi-escapes';
2
+ import prettyMs from 'pretty-ms';
3
+ import { getFullErrorInfo } from '../../error/parse/parse.js';
4
+ import { serializeLogError } from '../../error/parse/serialize_log.js';
5
+ import { roundTimerToMillisecs } from '../../time/measure.js';
6
+ import { ROOT_PACKAGE_JSON } from '../../utils/json.js';
7
+ import { getLogHeaderFunc } from '../header_func.js';
8
+ import { log, logMessage, logWarning, logHeader, logSubHeader, logWarningArray } from '../logger.js';
9
+ import { THEME } from '../theme.js';
10
+ import { logConfigOnError } from './config.js';
11
+ export const logBuildStart = function (logs) {
12
+ logHeader(logs, 'Netlify Build');
13
+ logSubHeader(logs, 'Version');
14
+ logMessage(logs, `${ROOT_PACKAGE_JSON.name} ${ROOT_PACKAGE_JSON.version}`);
15
+ };
16
+ export const logBuildError = function ({ error, netlifyConfig, logs, debug }) {
17
+ const fullErrorInfo = getFullErrorInfo({ error, colors: true, debug });
18
+ const { severity } = fullErrorInfo;
19
+ const { title, body } = serializeLogError({ fullErrorInfo });
20
+ const logHeaderFunc = getLogHeaderFunc(error);
21
+ logHeaderFunc(logs, title);
22
+ logMessage(logs, `\n${body}\n`);
23
+ logConfigOnError({ logs, netlifyConfig, severity });
24
+ };
25
+ export const logBuildSuccess = function (logs) {
26
+ logHeader(logs, 'Netlify Build Complete');
27
+ logMessage(logs, '');
28
+ };
29
+ export const logTimer = function (logs, durationNs, timerName, systemLog) {
30
+ const durationMs = roundTimerToMillisecs(durationNs);
31
+ const duration = prettyMs(durationMs);
32
+ log(logs, THEME.dimWords(`(${timerName} completed in ${duration})`));
33
+ systemLog(`Build step duration: ${timerName} completed in ${durationMs}ms`);
34
+ };
35
+ export const logMissingSideFile = function (logs, sideFile, publish) {
36
+ logWarning(logs, `
37
+ A "${sideFile}" file is present in the repository but is missing in the publish directory "${publish}".`);
38
+ };
39
+ // @todo use `terminal-link` (https://github.com/sindresorhus/terminal-link)
40
+ // instead of `ansi-escapes` once
41
+ // https://github.com/jamestalmage/supports-hyperlinks/pull/12 is fixed
42
+ export const logLingeringProcesses = function (logs, commands) {
43
+ logWarning(logs, `
44
+ The build completed successfully, but the following processes were still running:
45
+ `);
46
+ logWarningArray(logs, commands);
47
+ logWarning(logs, `
48
+ These processes have been terminated. In case this creates a problem for your build, refer to this ${ansiEscapes.link('article', 'https://answers.netlify.com/t/support-guide-how-to-address-the-warning-message-related-to-terminating-processes-in-builds/35277')} for details about why this process termination happens and how to fix it.`);
49
+ };