@netlify/config 19.0.0-rc → 19.0.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.
Files changed (100) hide show
  1. package/bin.js +5 -0
  2. package/lib/api/build_settings.js +28 -0
  3. package/lib/api/client.js +12 -0
  4. package/lib/api/site_info.js +59 -0
  5. package/{src → lib}/base.js +10 -18
  6. package/lib/bin/flags.js +173 -0
  7. package/lib/bin/main.js +59 -0
  8. package/{src → lib}/build_dir.js +11 -15
  9. package/{src → lib}/cached_config.js +14 -17
  10. package/lib/case.js +18 -0
  11. package/lib/context.js +86 -0
  12. package/lib/default.js +27 -0
  13. package/lib/env/envelope.js +24 -0
  14. package/lib/env/git.js +23 -0
  15. package/lib/env/main.js +150 -0
  16. package/lib/error.js +28 -0
  17. package/lib/events.js +21 -0
  18. package/lib/files.js +83 -0
  19. package/{src → lib}/functions_config.js +36 -52
  20. package/lib/headers.js +20 -0
  21. package/lib/inline_config.js +8 -0
  22. package/lib/log/cleanup.js +64 -0
  23. package/lib/log/logger.js +36 -0
  24. package/lib/log/main.js +39 -0
  25. package/{src → lib}/log/messages.js +56 -95
  26. package/lib/log/options.js +29 -0
  27. package/lib/log/serialize.js +4 -0
  28. package/lib/log/theme.js +13 -0
  29. package/lib/main.js +210 -0
  30. package/{src → lib}/merge.js +25 -35
  31. package/lib/merge_normalize.js +24 -0
  32. package/lib/mutations/apply.js +66 -0
  33. package/{src → lib}/mutations/config_prop_name.js +6 -8
  34. package/lib/mutations/update.js +98 -0
  35. package/lib/normalize.js +32 -0
  36. package/lib/options/base.js +54 -0
  37. package/lib/options/branch.js +31 -0
  38. package/{src → lib}/options/feature_flags.js +7 -10
  39. package/lib/options/main.js +91 -0
  40. package/lib/options/repository_root.js +16 -0
  41. package/lib/origin.js +31 -0
  42. package/lib/parse.js +56 -0
  43. package/lib/path.js +41 -0
  44. package/lib/redirects.js +19 -0
  45. package/lib/simplify.js +77 -0
  46. package/{src → lib}/utils/group.js +5 -6
  47. package/lib/utils/remove_falsy.js +14 -0
  48. package/lib/utils/set.js +27 -0
  49. package/lib/utils/toml.js +20 -0
  50. package/lib/validate/context.js +38 -0
  51. package/lib/validate/example.js +30 -0
  52. package/lib/validate/helpers.js +25 -0
  53. package/{src → lib}/validate/identical.js +8 -12
  54. package/lib/validate/main.js +99 -0
  55. package/lib/validate/validations.js +275 -0
  56. package/package.json +21 -12
  57. package/src/api/build_settings.js +0 -41
  58. package/src/api/client.js +0 -15
  59. package/src/api/site_info.js +0 -67
  60. package/src/bin/flags.js +0 -181
  61. package/src/bin/main.js +0 -73
  62. package/src/case.js +0 -37
  63. package/src/context.js +0 -99
  64. package/src/default.js +0 -31
  65. package/src/env/envelope.js +0 -24
  66. package/src/env/git.js +0 -23
  67. package/src/env/main.js +0 -198
  68. package/src/error.js +0 -36
  69. package/src/events.js +0 -22
  70. package/src/files.js +0 -107
  71. package/src/headers.js +0 -30
  72. package/src/inline_config.js +0 -9
  73. package/src/log/cleanup.js +0 -92
  74. package/src/log/logger.js +0 -47
  75. package/src/log/main.js +0 -48
  76. package/src/log/options.js +0 -43
  77. package/src/log/serialize.js +0 -5
  78. package/src/log/theme.js +0 -14
  79. package/src/main.js +0 -285
  80. package/src/merge_normalize.js +0 -31
  81. package/src/mutations/apply.js +0 -78
  82. package/src/mutations/update.js +0 -117
  83. package/src/normalize.js +0 -36
  84. package/src/options/base.js +0 -66
  85. package/src/options/branch.js +0 -34
  86. package/src/options/main.js +0 -111
  87. package/src/options/repository_root.js +0 -21
  88. package/src/origin.js +0 -38
  89. package/src/parse.js +0 -69
  90. package/src/path.js +0 -52
  91. package/src/redirects.js +0 -29
  92. package/src/simplify.js +0 -103
  93. package/src/utils/remove_falsy.js +0 -18
  94. package/src/utils/set.js +0 -33
  95. package/src/utils/toml.js +0 -23
  96. package/src/validate/context.js +0 -57
  97. package/src/validate/example.js +0 -37
  98. package/src/validate/helpers.js +0 -31
  99. package/src/validate/main.js +0 -143
  100. package/src/validate/validations.js +0 -289
@@ -0,0 +1,38 @@
1
+ import { warnContextPluginConfig, throwContextPluginsConfig } from '../log/messages.js';
2
+ import { UI_ORIGIN } from '../origin.js';
3
+ // The only reason to specify both `[[plugins]]` and
4
+ // `[[contexts.{context}.plugins]]` is to configure context-specific plugin
5
+ // inputs.
6
+ // The second cannot be used to enable a plugin for a specific context, because
7
+ // it would be overridden by `[[plugins]]`.
8
+ // We prevent against that mistake by printing a warning message when both are
9
+ // used. We only do this when `[[plugins]]` is due to plugin being UI-installed.
10
+ // We also fail the build when the following additional conditions apply:
11
+ // - `package` is the only property in `[[contexts.{context}.plugins]]`, i.e.
12
+ // there are no context-specific `inputs`
13
+ // - The current build is not in `context`
14
+ export const validateContextsPluginsConfig = function ({ contextProps, plugins, contexts, logs }) {
15
+ Object.entries(contextProps).forEach(([givenContext, givenContextProps]) => {
16
+ validateContextPluginsConfig({ givenContextProps, plugins, givenContext, contexts, logs });
17
+ });
18
+ };
19
+ const validateContextPluginsConfig = function ({ givenContextProps: { plugins: contextPlugins = [] }, plugins, givenContext, contexts, logs, }) {
20
+ contextPlugins.forEach((pluginConfig) => {
21
+ validateContextPluginConfig({ pluginConfig, plugins, givenContext, contexts, logs });
22
+ });
23
+ };
24
+ const validateContextPluginConfig = function ({ pluginConfig: { package: packageName, inputs = {} }, plugins = [], givenContext, contexts, logs, }) {
25
+ if (!isContextFreePlugin(plugins, packageName)) {
26
+ return;
27
+ }
28
+ if (isPluginConfigError(contexts, givenContext, inputs)) {
29
+ throwContextPluginsConfig(packageName, givenContext);
30
+ }
31
+ warnContextPluginConfig(logs, packageName, givenContext);
32
+ };
33
+ const isContextFreePlugin = function (plugins, packageName) {
34
+ return plugins.some((plugin) => plugin.package === packageName && plugin.origin === UI_ORIGIN);
35
+ };
36
+ const isPluginConfigError = function (contexts, givenContext, inputs) {
37
+ return contexts.every((context) => context !== givenContext) && Object.keys(inputs).length === 0;
38
+ };
@@ -0,0 +1,30 @@
1
+ import indentString from 'indent-string';
2
+ import { THEME } from '../log/theme.js';
3
+ import { serializeToml } from '../utils/toml.js';
4
+ // Print invalid value and example netlify.toml
5
+ export const getExample = function ({ value, key, prevPath, example, formatInvalid }) {
6
+ const exampleA = typeof example === 'function' ? example(value, key, prevPath) : example;
7
+ return `
8
+ ${THEME.errorSubHeader('Invalid syntax')}
9
+
10
+ ${indentString(getInvalidValue(value, prevPath, formatInvalid), 2)}
11
+
12
+ ${THEME.subHeader('Valid syntax')}
13
+
14
+ ${indentString(serializeToml(exampleA), 2)}`;
15
+ };
16
+ const getInvalidValue = function (value, prevPath, formatInvalid) {
17
+ // slice() is temporary, so it does not mutate
18
+ const invalidValue = [...prevPath].reverse().reduce(setInvalidValuePart, value);
19
+ // If `formatInvalid` is supplied, we use it to format the invalid value
20
+ // before serializing it to TOML and printing it.
21
+ const formattedInvalidValue = typeof formatInvalid === 'function' ? formatInvalid(invalidValue) : invalidValue;
22
+ const invalidValueA = serializeToml(formattedInvalidValue);
23
+ return invalidValueA;
24
+ };
25
+ const setInvalidValuePart = function (value, part) {
26
+ if (Number.isInteger(part)) {
27
+ return [value];
28
+ }
29
+ return value === undefined ? {} : { [part]: value };
30
+ };
@@ -0,0 +1,25 @@
1
+ import isPlainObj from 'is-plain-obj';
2
+ export const isArrayOfObjects = function (value) {
3
+ return Array.isArray(value) && value.every(isPlainObj);
4
+ };
5
+ export const isArrayOfStrings = function (value) {
6
+ return Array.isArray(value) && value.every(isString);
7
+ };
8
+ export const isString = function (value) {
9
+ return typeof value === 'string';
10
+ };
11
+ // Check an object valid properties, including legacy ones
12
+ export const validProperties = function (propNames, legacyPropNames) {
13
+ return {
14
+ check: (value) => checkValidProperty(value, [...propNames, ...legacyPropNames]),
15
+ message: `has unknown properties. Valid properties are:
16
+ ${propNames.map((propName) => ` - ${propName}`).join('\n')}`,
17
+ };
18
+ };
19
+ const checkValidProperty = function (value, propNames) {
20
+ return Object.keys(value).every((propName) => propNames.includes(propName));
21
+ };
22
+ export const functionsDirectoryCheck = {
23
+ formatInvalid: ({ functionsDirectory } = {}) => ({ functions: { directory: functionsDirectory } }),
24
+ propertyName: 'functions.directory',
25
+ };
@@ -1,20 +1,16 @@
1
- import { throwUserError } from '../error.js'
2
-
1
+ import { throwUserError } from '../error.js';
3
2
  // Validate that plugin are configured only once per origin
4
3
  // (`netlify.toml` or UI).
5
4
  // Exception: context-specific configuration since we allow context-specific
6
5
  // overrides. This does not validate them since contexts have not been merged
7
6
  // yet.
8
7
  export const validateIdenticalPlugins = function ({ plugins = [] }) {
9
- plugins.filter(hasIdenticalPlugin).forEach(throwIndenticalPlugin)
10
- }
11
-
8
+ plugins.filter(hasIdenticalPlugin).forEach(throwIndenticalPlugin);
9
+ };
12
10
  const hasIdenticalPlugin = function ({ package: packageName, origin }, index, plugins) {
13
- return plugins.slice(index + 1).some((pluginA) => packageName === pluginA.package && origin === pluginA.origin)
14
- }
15
-
11
+ return plugins.slice(index + 1).some((pluginA) => packageName === pluginA.package && origin === pluginA.origin);
12
+ };
16
13
  const throwIndenticalPlugin = function ({ package: packageName, origin }) {
17
- throwUserError(`Plugin "${packageName}" must not be specified twice in ${ORIGINS[origin]}`)
18
- }
19
-
20
- const ORIGINS = { config: 'netlify.toml', ui: 'the app' }
14
+ throwUserError(`Plugin "${packageName}" must not be specified twice in ${ORIGINS[origin]}`);
15
+ };
16
+ const ORIGINS = { config: 'netlify.toml', ui: 'the app' };
@@ -0,0 +1,99 @@
1
+ import { throwUserError } from '../error.js';
2
+ import { THEME } from '../log/theme.js';
3
+ import { getExample } from './example.js';
4
+ import { PRE_CASE_NORMALIZE_VALIDATIONS, PRE_MERGE_VALIDATIONS, PRE_CONTEXT_VALIDATIONS, PRE_NORMALIZE_VALIDATIONS, POST_NORMALIZE_VALIDATIONS, } from './validations.js';
5
+ // Validate the configuration file, before case normalization.
6
+ export const validatePreCaseNormalize = function (config) {
7
+ validateConfig(config, PRE_CASE_NORMALIZE_VALIDATIONS);
8
+ };
9
+ // Validate the configuration file, before `defaultConfig` merge.
10
+ export const validatePreMergeConfig = function (config) {
11
+ validateConfig(config, PRE_MERGE_VALIDATIONS);
12
+ };
13
+ // Validate the configuration file, before context merge.
14
+ export const validatePreContextConfig = function (config) {
15
+ validateConfig(config, PRE_CONTEXT_VALIDATIONS);
16
+ };
17
+ // Validate the configuration file, before normalization.
18
+ export const validatePreNormalizeConfig = function (config) {
19
+ validateConfig(config, PRE_NORMALIZE_VALIDATIONS);
20
+ };
21
+ // Validate the configuration file, after normalization.
22
+ export const validatePostNormalizeConfig = function (config) {
23
+ validateConfig(config, POST_NORMALIZE_VALIDATIONS);
24
+ };
25
+ const validateConfig = function (config, validations) {
26
+ try {
27
+ validations.forEach(({ property, ...validation }) => {
28
+ validateProperty(config, { ...validation, nextPath: property.split('.') });
29
+ });
30
+ }
31
+ catch (error) {
32
+ throwUserError(error);
33
+ }
34
+ };
35
+ // Validate a single property in the configuration file.
36
+ const validateProperty = function (parent, { nextPath: [propName, nextPropName, ...nextPath], prevPath = [propName], propPath = propName, key = propName, check, message, example, formatInvalid, propertyName, }) {
37
+ const value = parent[propName];
38
+ if (nextPropName !== undefined) {
39
+ return validateChild({
40
+ value,
41
+ nextPropName,
42
+ prevPath,
43
+ nextPath,
44
+ propPath,
45
+ key,
46
+ check,
47
+ message,
48
+ example,
49
+ formatInvalid,
50
+ propertyName,
51
+ });
52
+ }
53
+ if (value === undefined || (check !== undefined && check(value, key, prevPath))) {
54
+ return;
55
+ }
56
+ reportError({ prevPath, propPath, message, example, value, key, formatInvalid, propertyName });
57
+ };
58
+ const reportError = function ({ prevPath, propPath, message, example, value, key, formatInvalid, propertyName = propPath, }) {
59
+ throwUserError(`${THEME.highlightWords('Configuration property')} ${propertyName} ${message}
60
+ ${getExample({ value, key, prevPath, example, formatInvalid })}`);
61
+ };
62
+ // Recurse over children (each part of the `property` array).
63
+ const validateChild = function ({ value, nextPropName, prevPath, nextPath, propPath, ...rest }) {
64
+ if (value === undefined) {
65
+ return;
66
+ }
67
+ if (nextPropName !== '*') {
68
+ return validateProperty(value, {
69
+ ...rest,
70
+ prevPath: [...prevPath, nextPropName],
71
+ nextPath: [nextPropName, ...nextPath],
72
+ propPath: `${propPath}.${nextPropName}`,
73
+ key: nextPropName,
74
+ });
75
+ }
76
+ return Object.keys(value).forEach((childProp) => {
77
+ validateChildProp({ childProp, value, nextPath, propPath, prevPath, ...rest });
78
+ });
79
+ };
80
+ // Can use * to recurse over array|object elements.
81
+ const validateChildProp = function ({ childProp, value, nextPath, propPath, prevPath, ...rest }) {
82
+ if (Array.isArray(value)) {
83
+ const key = Number(childProp);
84
+ return validateProperty(value, {
85
+ ...rest,
86
+ prevPath: [...prevPath, key],
87
+ nextPath: [key, ...nextPath],
88
+ propPath: `${propPath}[${childProp}]`,
89
+ key,
90
+ });
91
+ }
92
+ validateProperty(value, {
93
+ ...rest,
94
+ prevPath: [...prevPath, childProp],
95
+ nextPath: [childProp, ...nextPath],
96
+ propPath: `${propPath}.${childProp}`,
97
+ key: childProp,
98
+ });
99
+ };
@@ -0,0 +1,275 @@
1
+ import CronParser from 'cron-parser';
2
+ import isPlainObj from 'is-plain-obj';
3
+ import validateNpmPackageName from 'validate-npm-package-name';
4
+ import { bundlers, WILDCARD_ALL as FUNCTIONS_CONFIG_WILDCARD_ALL } from '../functions_config.js';
5
+ import { functionsDirectoryCheck, isArrayOfObjects, isArrayOfStrings, isString, validProperties } from './helpers.js';
6
+ /**
7
+ * @param {string} cron
8
+ * @returns {boolean}
9
+ */
10
+ const isValidCronExpression = (cron) => {
11
+ try {
12
+ CronParser.parseExpression(cron);
13
+ return true;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ };
19
+ // List of validations performed on the configuration file.
20
+ // Validation are performed in order: parent should be before children.
21
+ // Each validation is an object with the following properties:
22
+ // - `property` {string}: dot-delimited path to the property.
23
+ // Can contain `*` providing a previous check validates the parent is an
24
+ // object or an array.
25
+ // - `propertyName` {string}: human-friendly property name; overrides the
26
+ // value of `property` when displaying an error message
27
+ // - `check` {(value, key, prevPath) => boolean}: validation check function
28
+ // - `message` {string}: error message
29
+ // - `example` {string}: example of correct code
30
+ // - `formatInvalid` {(object) => object}: formats the invalid value when
31
+ // displaying an error message
32
+ // We use this instead of JSON schema (or others) to get nicer error messages.
33
+ // Validations done before case normalization
34
+ export const PRE_CASE_NORMALIZE_VALIDATIONS = [
35
+ {
36
+ property: 'build',
37
+ check: isPlainObj,
38
+ message: 'must be a plain object.',
39
+ example: () => ({ build: { command: 'npm run build' } }),
40
+ },
41
+ ];
42
+ // Properties with an `origin` property need to be validated twice:
43
+ // - Before the `origin` property is added
44
+ // - After `context.*` is merged, since they might contain that property
45
+ const ORIGIN_VALIDATIONS = [
46
+ {
47
+ property: 'build.command',
48
+ check: isString,
49
+ message: 'must be a string',
50
+ example: () => ({ build: { command: 'npm run build' } }),
51
+ },
52
+ {
53
+ property: 'plugins',
54
+ check: isArrayOfObjects,
55
+ message: 'must be an array of objects.',
56
+ example: () => ({ plugins: [{ package: 'netlify-plugin-one' }, { package: 'netlify-plugin-two' }] }),
57
+ },
58
+ ];
59
+ // Validations done before `defaultConfig` merge
60
+ export const PRE_MERGE_VALIDATIONS = [...ORIGIN_VALIDATIONS];
61
+ // Validations done before context merge
62
+ export const PRE_CONTEXT_VALIDATIONS = [
63
+ {
64
+ property: 'context',
65
+ check: isPlainObj,
66
+ message: 'must be a plain object.',
67
+ example: () => ({ context: { production: { publish: 'dist' } } }),
68
+ },
69
+ {
70
+ property: 'context.*',
71
+ check: isPlainObj,
72
+ message: 'must be a plain object.',
73
+ example: (contextProps, key) => ({ context: { [key]: { publish: 'dist' } } }),
74
+ },
75
+ ];
76
+ // Validations done before normalization
77
+ export const PRE_NORMALIZE_VALIDATIONS = [
78
+ ...ORIGIN_VALIDATIONS,
79
+ {
80
+ property: 'functions',
81
+ check: isPlainObj,
82
+ message: 'must be an object.',
83
+ example: () => ({
84
+ functions: { external_node_modules: ['module-one', 'module-two'] },
85
+ }),
86
+ },
87
+ {
88
+ property: 'functions',
89
+ check: isPlainObj,
90
+ message: 'must be an object.',
91
+ example: () => ({
92
+ functions: { ignored_node_modules: ['module-one', 'module-two'] },
93
+ }),
94
+ },
95
+ {
96
+ property: 'edge_functions',
97
+ check: isArrayOfObjects,
98
+ message: 'must be an array of objects.',
99
+ example: () => ({
100
+ edge_functions: [
101
+ { path: '/hello', function: 'hello' },
102
+ { path: '/auth', function: 'auth' },
103
+ ],
104
+ }),
105
+ },
106
+ ];
107
+ const EXAMPLE_PORT = 80;
108
+ // Validations done after normalization
109
+ export const POST_NORMALIZE_VALIDATIONS = [
110
+ {
111
+ property: 'plugins.*',
112
+ ...validProperties(['package', 'pinned_version', 'inputs'], ['origin']),
113
+ example: { plugins: [{ package: 'netlify-plugin-one', inputs: { port: EXAMPLE_PORT } }] },
114
+ },
115
+ {
116
+ property: 'plugins.*',
117
+ check: (plugin) => plugin.package !== undefined,
118
+ message: '"package" property is required.',
119
+ example: () => ({ plugins: [{ package: 'netlify-plugin-one' }] }),
120
+ },
121
+ {
122
+ property: 'plugins.*.package',
123
+ check: isString,
124
+ message: 'must be a string.',
125
+ example: () => ({ plugins: [{ package: 'netlify-plugin-one' }] }),
126
+ },
127
+ // We don't allow `package@tag|version` nor `git:...`, `github:...`,
128
+ // `https://...`, etc.
129
+ // We skip this validation for local plugins.
130
+ // We ensure @scope/plugin still work.
131
+ {
132
+ property: 'plugins.*.package',
133
+ check: (packageName) => packageName.startsWith('.') ||
134
+ packageName.startsWith('/') ||
135
+ validateNpmPackageName(packageName).validForOldPackages,
136
+ message: 'must be a npm package name only.',
137
+ example: () => ({ plugins: [{ package: 'netlify-plugin-one' }] }),
138
+ },
139
+ {
140
+ property: 'plugins.*.pinned_version',
141
+ check: isString,
142
+ message: 'must be a string.',
143
+ example: () => ({ plugins: [{ package: 'netlify-plugin-one', pinned_version: '1' }] }),
144
+ },
145
+ {
146
+ property: 'plugins.*.inputs',
147
+ check: isPlainObj,
148
+ message: 'must be a plain object.',
149
+ example: () => ({ plugins: [{ package: 'netlify-plugin-one', inputs: { port: EXAMPLE_PORT } }] }),
150
+ },
151
+ {
152
+ property: 'build.base',
153
+ check: isString,
154
+ message: 'must be a string.',
155
+ example: () => ({ build: { base: 'packages/project' } }),
156
+ },
157
+ {
158
+ property: 'build.publish',
159
+ check: isString,
160
+ message: 'must be a string.',
161
+ example: () => ({ build: { publish: 'dist' } }),
162
+ },
163
+ {
164
+ property: 'build.functions',
165
+ check: isString,
166
+ message: 'must be a string.',
167
+ example: () => ({ build: { functions: 'functions' } }),
168
+ },
169
+ {
170
+ property: 'build.edge_functions',
171
+ check: isString,
172
+ message: 'must be a string.',
173
+ example: () => ({ build: { edge_functions: 'edge-functions' } }),
174
+ },
175
+ {
176
+ property: 'functions.*',
177
+ check: isPlainObj,
178
+ message: 'must be an object.',
179
+ example: (value, key, prevPath) => ({
180
+ functions: { [prevPath[1]]: { external_node_modules: ['module-one', 'module-two'] } },
181
+ }),
182
+ },
183
+ {
184
+ property: 'functions.*.external_node_modules',
185
+ check: isArrayOfStrings,
186
+ message: 'must be an array of strings.',
187
+ example: (value, key, prevPath) => ({
188
+ functions: { [prevPath[1]]: { external_node_modules: ['module-one', 'module-two'] } },
189
+ }),
190
+ },
191
+ {
192
+ property: 'functions.*.ignored_node_modules',
193
+ check: isArrayOfStrings,
194
+ message: 'must be an array of strings.',
195
+ example: (value, key, prevPath) => ({
196
+ functions: { [prevPath[1]]: { ignored_node_modules: ['module-one', 'module-two'] } },
197
+ }),
198
+ },
199
+ {
200
+ property: 'functions.*.included_files',
201
+ check: isArrayOfStrings,
202
+ message: 'must be an array of strings.',
203
+ example: (value, key, prevPath) => ({
204
+ functions: { [prevPath[1]]: { included_files: ['directory-one/file1', 'directory-two/**/*.jpg'] } },
205
+ }),
206
+ },
207
+ {
208
+ property: 'functions.*.node_bundler',
209
+ check: (value) => bundlers.includes(value),
210
+ message: `must be one of: ${bundlers.join(', ')}`,
211
+ example: (value, key, prevPath) => ({
212
+ functions: { [prevPath[1]]: { node_bundler: bundlers[0] } },
213
+ }),
214
+ },
215
+ {
216
+ property: 'functions.*.directory',
217
+ check: (value, key, prevPath) => prevPath[1] === FUNCTIONS_CONFIG_WILDCARD_ALL,
218
+ message: 'must be defined on the main `functions` object.',
219
+ example: () => ({
220
+ functions: { directory: 'my-functions' },
221
+ }),
222
+ },
223
+ {
224
+ property: 'functions.*.schedule',
225
+ check: isValidCronExpression,
226
+ message: 'must be a valid cron expression (see https://ntl.fyi/cron-syntax).',
227
+ example: (value, key, prevPath) => ({
228
+ functions: { [prevPath[1]]: { schedule: '5 4 * * *' } },
229
+ }),
230
+ },
231
+ {
232
+ property: 'functionsDirectory',
233
+ check: isString,
234
+ message: 'must be a string.',
235
+ ...functionsDirectoryCheck,
236
+ example: () => ({
237
+ functions: { directory: 'my-functions' },
238
+ }),
239
+ },
240
+ {
241
+ property: 'edge_functions.*',
242
+ ...validProperties(['path', 'function'], []),
243
+ example: () => ({ edge_functions: [{ path: '/hello', function: 'hello' }] }),
244
+ },
245
+ {
246
+ property: 'edge_functions.*',
247
+ check: (edgeFunction) => edgeFunction.path !== undefined,
248
+ message: '"path" property is required.',
249
+ example: () => ({ edge_functions: [{ path: '/hello', function: 'hello' }] }),
250
+ },
251
+ {
252
+ property: 'edge_functions.*',
253
+ check: (edgeFunction) => edgeFunction.function !== undefined,
254
+ message: '"function" property is required.',
255
+ example: () => ({ edge_functions: [{ path: '/hello', function: 'hello' }] }),
256
+ },
257
+ {
258
+ property: 'edge_functions.*.path',
259
+ check: isString,
260
+ message: 'must be a string.',
261
+ example: () => ({ edge_functions: [{ path: '/hello', function: 'hello' }] }),
262
+ },
263
+ {
264
+ property: 'edge_functions.*.function',
265
+ check: isString,
266
+ message: 'must be a string.',
267
+ example: () => ({ edge_functions: [{ path: '/hello', function: 'hello' }] }),
268
+ },
269
+ {
270
+ property: 'edge_functions.*.path',
271
+ check: (pathName) => pathName.startsWith('/'),
272
+ message: 'must be a valid path.',
273
+ example: () => ({ edge_functions: [{ path: '/hello', function: 'hello' }] }),
274
+ },
275
+ ];
package/package.json CHANGED
@@ -1,22 +1,27 @@
1
1
  {
2
2
  "name": "@netlify/config",
3
- "version": "19.0.0-rc",
3
+ "version": "19.0.0",
4
4
  "description": "Netlify config module",
5
5
  "type": "module",
6
- "exports": "./src/main.js",
7
- "main": "./src/main.js",
6
+ "exports": "./lib/main.js",
7
+ "main": "./lib/main.js",
8
+ "types": "./lib/main.d.ts",
8
9
  "bin": {
9
- "netlify-config": "src/bin/main.js"
10
+ "netlify-config": "./bin.js"
10
11
  },
11
12
  "files": [
12
- "src/**/*.js"
13
+ "bin.js",
14
+ "lib/**/*.js"
13
15
  ],
14
16
  "author": "Netlify Inc.",
15
17
  "contributors": [
16
18
  "David Wells <hello@davidwells.io> (https://davidwells.io/)"
17
19
  ],
18
20
  "scripts": {
19
- "prepublishOnly": "cd ../../ && npm run prepublishOnly"
21
+ "prebuild": "rm -rf lib",
22
+ "build": "tsc",
23
+ "test": "ava",
24
+ "test:ci": "c8 -r lcovonly -r text -r json ava"
20
25
  },
21
26
  "keywords": [
22
27
  "javascript",
@@ -64,9 +69,9 @@
64
69
  "is-plain-obj": "^4.0.0",
65
70
  "js-yaml": "^4.0.0",
66
71
  "map-obj": "^5.0.0",
67
- "netlify": "^12.0.0",
68
- "netlify-headers-parser": "^6.0.2",
69
- "netlify-redirect-parser": "13.0.5",
72
+ "netlify": "^13.0.0",
73
+ "netlify-headers-parser": "^7.0.0",
74
+ "netlify-redirect-parser": "^14.0.0",
70
75
  "omit.js": "^2.0.2",
71
76
  "p-locate": "^6.0.0",
72
77
  "path-exists": "^5.0.0",
@@ -77,13 +82,17 @@
77
82
  "yargs": "^17.3.1"
78
83
  },
79
84
  "devDependencies": {
85
+ "@types/node": "^14.18.31",
80
86
  "ava": "^4.0.0",
87
+ "c8": "^7.12.0",
81
88
  "del": "^6.0.0",
82
89
  "has-ansi": "^5.0.0",
83
90
  "is-ci": "^3.0.0",
84
- "tmp-promise": "^3.0.2"
91
+ "tmp-promise": "^3.0.2",
92
+ "typescript": "^4.8.4"
85
93
  },
86
94
  "engines": {
87
- "node": "^12.20.0 || ^14.14.0 || >=16.0.0"
88
- }
95
+ "node": "^14.16.0 || >=16.0.0"
96
+ },
97
+ "gitHead": "c4502f1c112e7f33d5e6fa053bfbe7dabdfe0160"
89
98
  }
@@ -1,41 +0,0 @@
1
- import { removeFalsy } from '../utils/remove_falsy.js'
2
-
3
- // Netlify UI build settings are used as default configuration values in
4
- // production. In local builds, we retrieve them with `getSite` (`siteInfo`)
5
- // instead.
6
- // This also includes UI-installed plugins.
7
- export const addBuildSettings = function ({
8
- defaultConfig,
9
- baseRelDir,
10
- siteInfo: { build_settings: buildSettings, plugins },
11
- }) {
12
- if (buildSettings === undefined) {
13
- return { defaultConfig, baseRelDir }
14
- }
15
-
16
- const defaultConfigA = getDefaultConfig(buildSettings, defaultConfig, plugins)
17
- const baseRelDirA = getBaseRelDir(buildSettings, baseRelDir)
18
- return { defaultConfig: defaultConfigA, baseRelDir: baseRelDirA }
19
- }
20
-
21
- // From the `getSite` API response to the corresponding configuration properties
22
- const getDefaultConfig = function (
23
- { cmd: command, dir: publish, functions_dir: functionsDirectory, base },
24
- { build, plugins = [], ...defaultConfig },
25
- uiPlugins = [],
26
- ) {
27
- const siteBuild = removeFalsy({ command, publish, base })
28
- const functions = functionsDirectory ? { functionsDirectory, functionsDirectoryOrigin: 'ui' } : {}
29
- const uiPluginsA = uiPlugins.map(normalizeUiPlugin)
30
- const pluginsA = [...uiPluginsA, ...plugins]
31
- return { ...defaultConfig, build: { ...siteBuild, ...build }, plugins: pluginsA, ...functions }
32
- }
33
-
34
- // Make sure we know which fields we are picking from the API
35
- const normalizeUiPlugin = function ({ package: packageName, inputs, pinned_version: pinnedVersion }) {
36
- return { package: packageName, inputs, pinned_version: pinnedVersion }
37
- }
38
-
39
- const getBaseRelDir = function ({ base_rel_dir: siteBaseRelDir }, baseRelDir = siteBaseRelDir) {
40
- return Boolean(baseRelDir)
41
- }
package/src/api/client.js DELETED
@@ -1,15 +0,0 @@
1
- import { NetlifyAPI } from 'netlify'
2
-
3
- import { removeUndefined } from '../utils/remove_falsy.js'
4
-
5
- // Retrieve Netlify API client, if an access token was passed
6
- export const getApiClient = function ({ token, offline, testOpts = {}, host, scheme, pathPrefix }) {
7
- if (!token || offline) {
8
- return
9
- }
10
-
11
- // TODO: find less intrusive way to mock HTTP requests
12
- const parameters = removeUndefined({ scheme: testOpts.scheme || scheme, host: testOpts.host || host, pathPrefix })
13
- const api = new NetlifyAPI(token, parameters)
14
- return api
15
- }