@netlify/config 20.7.0 → 20.8.0-rc.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/base.js +22 -14
- package/lib/bin/flags.js +4 -0
- package/lib/build_dir.js +13 -9
- package/lib/files.js +57 -33
- package/lib/main.js +47 -27
- package/lib/merge_normalize.js +11 -7
- package/lib/normalize.js +13 -9
- package/lib/path.js +8 -8
- package/lib/redirects.js +4 -3
- package/lib/utils/remove_falsy.js +7 -11
- package/package.json +3 -4
package/lib/base.js
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
import { resolvePath } from './files.js';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Retrieve the first `base` directory used to load the first config file.
|
|
4
|
+
*/
|
|
5
|
+
export const getInitialBase = function ({ repositoryRoot,
|
|
6
|
+
// @ts-expect-error TODO: enhance later
|
|
7
|
+
defaultConfig: { build: { base: defaultBase } = {} }, inlineConfig: { build: { base: initialBase = defaultBase } = {} }, }) {
|
|
4
8
|
return resolveBase(repositoryRoot, initialBase);
|
|
5
9
|
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Two config files can be used:
|
|
12
|
+
* - The first one, using the `config` property or doing a default lookup
|
|
13
|
+
* of `netlify.toml`
|
|
14
|
+
* - If the first one has a `base` property pointing to a directory with
|
|
15
|
+
* another `netlify.toml`, that second config file is used instead.
|
|
16
|
+
* This retrieves the final `base` directory used:
|
|
17
|
+
* - To load the second config file
|
|
18
|
+
* - As the `buildDir`
|
|
19
|
+
* - To resolve file paths
|
|
20
|
+
* If the second file has a `base` property, it is ignored, i.e. it is not
|
|
21
|
+
* recursive.
|
|
22
|
+
*/
|
|
17
23
|
export const getBase = function (base, repositoryRoot, config) {
|
|
18
24
|
return base === undefined ? resolveBase(repositoryRoot, config.build.base) : base;
|
|
19
25
|
};
|
|
20
26
|
const resolveBase = function (repositoryRoot, base) {
|
|
21
27
|
return resolvePath(repositoryRoot, repositoryRoot, base, 'build.base');
|
|
22
28
|
};
|
|
23
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Also `config.build.base`.
|
|
31
|
+
*/
|
|
24
32
|
export const addBase = function (config, base) {
|
|
25
33
|
return { ...config, build: { ...config.build, base } };
|
|
26
34
|
};
|
package/lib/bin/flags.js
CHANGED
|
@@ -72,6 +72,10 @@ Default: empty array.`,
|
|
|
72
72
|
describe: `Current directory. Used to retrieve the configuration file.
|
|
73
73
|
Default: current directory`,
|
|
74
74
|
},
|
|
75
|
+
packagePath: {
|
|
76
|
+
string: true,
|
|
77
|
+
describe: `A relative path from the repository root to the package. Used inside monorepos to specify a package`,
|
|
78
|
+
},
|
|
75
79
|
repositoryRoot: {
|
|
76
80
|
string: true,
|
|
77
81
|
describe: `Git repository root directory. Used to retrieve the configuration file.
|
package/lib/build_dir.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { isDirectory } from 'path-type';
|
|
2
2
|
import { throwUserError } from './error.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve the build directory used to resolve most paths.
|
|
5
|
+
* This is (in priority order):
|
|
6
|
+
* - `build.base`
|
|
7
|
+
* - `--repositoryRoot`
|
|
8
|
+
* - the current directory (default value of `--repositoryRoot`)
|
|
9
|
+
*/
|
|
8
10
|
export const getBuildDir = async function (repositoryRoot, base) {
|
|
9
11
|
const buildDir = base === undefined ? repositoryRoot : base;
|
|
10
12
|
await checkBuildDir(buildDir, repositoryRoot);
|
|
11
13
|
return buildDir;
|
|
12
14
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The build directory is used as the current directory of build commands and
|
|
17
|
+
* build plugins. Therefore, it must exist.
|
|
18
|
+
* We already check `repositoryRoot` earlier in the code, so only need to check
|
|
19
|
+
* `buildDir` when it is the base directory instead.
|
|
20
|
+
*/
|
|
17
21
|
const checkBuildDir = async function (buildDir, repositoryRoot) {
|
|
18
22
|
if (buildDir === repositoryRoot || (await isDirectory(buildDir))) {
|
|
19
23
|
return;
|
package/lib/files.js
CHANGED
|
@@ -1,63 +1,87 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
|
-
import { resolve, relative, parse } from 'path';
|
|
2
|
+
import { resolve, relative, parse, join } from 'path';
|
|
3
3
|
import { getProperty, setProperty, deleteProperty } from 'dot-prop';
|
|
4
4
|
import { throwUserError } from './error.js';
|
|
5
5
|
import { mergeConfigs } from './merge.js';
|
|
6
6
|
import { isTruthy } from './utils/remove_falsy.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// (if `baseRelDir` is `true`).
|
|
7
|
+
/**
|
|
8
|
+
* We allow paths in configuration file to start with /
|
|
9
|
+
* In that case, those are actually relative paths not absolute.
|
|
10
|
+
*/
|
|
11
|
+
const LEADING_SLASH_REGEXP = /^\/+/;
|
|
12
|
+
/**
|
|
13
|
+
* All file paths in the configuration file are relative to `buildDir`
|
|
14
|
+
* (if `baseRelDir` is `true`).
|
|
15
|
+
*/
|
|
17
16
|
const FILE_PATH_CONFIG_PROPS = [
|
|
18
17
|
'functionsDirectory',
|
|
19
18
|
'functions.*.deno_import_map',
|
|
20
19
|
'build.publish',
|
|
21
20
|
'build.edge_functions',
|
|
22
21
|
];
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Make configuration paths relative to `buildDir` and converts them to
|
|
24
|
+
* absolute paths
|
|
25
|
+
*/
|
|
26
|
+
export const resolveConfigPaths = function (options) {
|
|
27
|
+
const baseRel = options.baseRelDir ? options.buildDir : options.repositoryRoot;
|
|
28
|
+
const config = resolvePaths({
|
|
29
|
+
config: options.config,
|
|
30
|
+
packagePath: options.packagePath,
|
|
31
|
+
propNames: FILE_PATH_CONFIG_PROPS,
|
|
32
|
+
baseRel,
|
|
33
|
+
repositoryRoot: options.repositoryRoot,
|
|
34
|
+
});
|
|
35
|
+
return addDefaultPaths({
|
|
36
|
+
config,
|
|
37
|
+
repositoryRoot: options.repositoryRoot,
|
|
38
|
+
baseRel,
|
|
39
|
+
packagePath: options.packagePath,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const resolvePaths = function ({ config, propNames, baseRel, packagePath, repositoryRoot, }) {
|
|
43
|
+
return propNames.reduce((configA, propName) => resolvePathProp(configA, propName, baseRel, repositoryRoot, packagePath), config);
|
|
25
44
|
};
|
|
26
|
-
const resolvePathProp = function (config, propName, baseRel, repositoryRoot) {
|
|
45
|
+
const resolvePathProp = function (config, propName, baseRel, repositoryRoot, packagePath) {
|
|
27
46
|
const path = getProperty(config, propName);
|
|
28
47
|
if (!isTruthy(path)) {
|
|
29
48
|
deleteProperty(config, propName);
|
|
30
49
|
return config;
|
|
31
50
|
}
|
|
32
|
-
return setProperty(config, propName, resolvePath(repositoryRoot, baseRel, path, propName));
|
|
51
|
+
return setProperty(config, propName, resolvePath(repositoryRoot, baseRel, path, propName, packagePath));
|
|
33
52
|
};
|
|
34
|
-
export const resolvePath =
|
|
53
|
+
export const resolvePath = (repositoryRoot, baseRel, originalPath, propName,
|
|
54
|
+
// @ts-expect-error depends on the survey outcome see comment below
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
56
|
+
packagePath) => {
|
|
35
57
|
if (!isTruthy(originalPath)) {
|
|
36
58
|
return;
|
|
37
59
|
}
|
|
38
60
|
const path = originalPath.replace(LEADING_SLASH_REGEXP, '');
|
|
61
|
+
// TODO: Based on survey outcome: https://netlify.slack.com/archives/C05556LEX28/p1691423437855069
|
|
62
|
+
// If we like option B then:
|
|
63
|
+
// const pathA = resolve(baseRel, packagePath || '', path)
|
|
39
64
|
const pathA = resolve(baseRel, path);
|
|
40
65
|
validateInsideRoot(originalPath, pathA, repositoryRoot, propName);
|
|
41
66
|
return pathA;
|
|
42
67
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const validateInsideRoot = function (originalPath, path, repositoryRoot, propName) {
|
|
68
|
+
/**
|
|
69
|
+
* We ensure all file paths are within the repository root directory.
|
|
70
|
+
* However, we allow file paths to be outside the build directory, since this
|
|
71
|
+
* can be convenient in monorepo setups.
|
|
72
|
+
*/
|
|
73
|
+
const validateInsideRoot = (originalPath, path, repositoryRoot, propName) => {
|
|
50
74
|
if (relative(repositoryRoot, path).startsWith('..') || getWindowsDrive(repositoryRoot) !== getWindowsDrive(path)) {
|
|
51
75
|
throwUserError(`Configuration property "${propName}" "${originalPath}" must be inside the repository root directory.`);
|
|
52
76
|
}
|
|
53
77
|
};
|
|
54
|
-
const getWindowsDrive =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const addDefaultPaths =
|
|
60
|
-
const defaultPathsConfigs = DEFAULT_PATHS.map(({ defaultPath, getConfig, propName }) => addDefaultPath({ repositoryRoot, baseRel, defaultPath, getConfig, propName })).filter(Boolean);
|
|
78
|
+
const getWindowsDrive = (path) => parse(path).root;
|
|
79
|
+
/**
|
|
80
|
+
* Some configuration properties have default values that are only set if a
|
|
81
|
+
* specific directory/file exists in the build directory
|
|
82
|
+
*/
|
|
83
|
+
const addDefaultPaths = ({ config, repositoryRoot, baseRel, packagePath, }) => {
|
|
84
|
+
const defaultPathsConfigs = DEFAULT_PATHS.map(({ defaultPath, getConfig, propName }) => addDefaultPath({ repositoryRoot, packagePath, baseRel, defaultPath, getConfig, propName })).filter(Boolean);
|
|
61
85
|
return mergeConfigs([...defaultPathsConfigs, config]);
|
|
62
86
|
};
|
|
63
87
|
const DEFAULT_PATHS = [
|
|
@@ -78,9 +102,9 @@ const DEFAULT_PATHS = [
|
|
|
78
102
|
propName: 'build.edge_functions',
|
|
79
103
|
},
|
|
80
104
|
];
|
|
81
|
-
const addDefaultPath =
|
|
82
|
-
const absolutePath = resolvePath(repositoryRoot, baseRel, defaultPath, propName);
|
|
83
|
-
if (!existsSync(absolutePath)) {
|
|
105
|
+
const addDefaultPath = ({ repositoryRoot, packagePath, baseRel, defaultPath, getConfig, propName, }) => {
|
|
106
|
+
const absolutePath = resolvePath(repositoryRoot, join(baseRel, packagePath || ''), defaultPath, propName);
|
|
107
|
+
if (!absolutePath || !existsSync(absolutePath)) {
|
|
84
108
|
return;
|
|
85
109
|
}
|
|
86
110
|
return getConfig(absolutePath);
|
package/lib/main.js
CHANGED
|
@@ -17,11 +17,13 @@ import { UI_ORIGIN, CONFIG_ORIGIN, INLINE_ORIGIN } from './origin.js';
|
|
|
17
17
|
import { parseConfig } from './parse.js';
|
|
18
18
|
import { getConfigPath } from './path.js';
|
|
19
19
|
import { getRedirectsPath, addRedirects } from './redirects.js';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Load the configuration file.
|
|
22
|
+
* Takes an optional configuration file path as input and return the resolved
|
|
23
|
+
* `config` together with related properties such as the `configPath`.
|
|
24
|
+
*/
|
|
23
25
|
export const resolveConfig = async function (opts) {
|
|
24
|
-
const { cachedConfig, cachedConfigPath, host, scheme, pathPrefix, testOpts, token, offline, siteFeatureFlagPrefix, ...optsA } = addDefaultOpts(opts);
|
|
26
|
+
const { cachedConfig, cachedConfigPath, host, scheme, packagePath, pathPrefix, testOpts, token, offline, siteFeatureFlagPrefix, ...optsA } = addDefaultOpts(opts);
|
|
25
27
|
// `api` is not JSON-serializable, so we cannot cache it inside `cachedConfig`
|
|
26
28
|
const api = getApiClient({ token, offline, host, scheme, pathPrefix, testOpts });
|
|
27
29
|
const parsedCachedConfig = await getCachedConfig({ cachedConfig, cachedConfigPath, token, api });
|
|
@@ -52,6 +54,7 @@ export const resolveConfig = async function (opts) {
|
|
|
52
54
|
cwd,
|
|
53
55
|
context,
|
|
54
56
|
repositoryRoot,
|
|
57
|
+
packagePath,
|
|
55
58
|
branch,
|
|
56
59
|
defaultConfig: defaultConfigA,
|
|
57
60
|
inlineConfig: inlineConfigA,
|
|
@@ -95,8 +98,10 @@ export const resolveConfig = async function (opts) {
|
|
|
95
98
|
logResult(result, { logs, debug });
|
|
96
99
|
return result;
|
|
97
100
|
};
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Adds a `build.functions` property that mirrors `functionsDirectory`, for
|
|
103
|
+
* backward compatibility.
|
|
104
|
+
*/
|
|
100
105
|
const addLegacyFunctionsDirectory = (config) => {
|
|
101
106
|
if (!config.functionsDirectory) {
|
|
102
107
|
return config;
|
|
@@ -109,10 +114,12 @@ const addLegacyFunctionsDirectory = (config) => {
|
|
|
109
114
|
},
|
|
110
115
|
};
|
|
111
116
|
};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Try to load the configuration file in two passes.
|
|
119
|
+
* The first pass uses the `defaultConfig`'s `build.base` (if defined).
|
|
120
|
+
* The second pass uses the `build.base` from the first pass (if defined).
|
|
121
|
+
*/
|
|
122
|
+
const loadConfig = async function ({ configOpt, cwd, context, repositoryRoot, packagePath, branch, defaultConfig, inlineConfig, baseRelDir, logs, featureFlags, }) {
|
|
116
123
|
const initialBase = getInitialBase({ repositoryRoot, defaultConfig, inlineConfig });
|
|
117
124
|
const { configPath, config, buildDir, base, redirectsPath, headersPath } = await getFullConfig({
|
|
118
125
|
configOpt,
|
|
@@ -123,6 +130,7 @@ const loadConfig = async function ({ configOpt, cwd, context, repositoryRoot, br
|
|
|
123
130
|
defaultConfig,
|
|
124
131
|
inlineConfig,
|
|
125
132
|
baseRelDir,
|
|
133
|
+
packagePath,
|
|
126
134
|
configBase: initialBase,
|
|
127
135
|
logs,
|
|
128
136
|
featureFlags,
|
|
@@ -158,9 +166,17 @@ const loadConfig = async function ({ configOpt, cwd, context, repositoryRoot, br
|
|
|
158
166
|
headersPath: headersPathA,
|
|
159
167
|
};
|
|
160
168
|
};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Load configuration file and normalize it, merge contexts, etc.
|
|
171
|
+
*/
|
|
172
|
+
const getFullConfig = async function ({ configOpt, cwd, context, repositoryRoot, packagePath, branch, defaultConfig, inlineConfig, baseRelDir, configBase, base, logs, featureFlags, }) {
|
|
173
|
+
const configPath = await getConfigPath({
|
|
174
|
+
configOpt,
|
|
175
|
+
cwd,
|
|
176
|
+
repositoryRoot,
|
|
177
|
+
packagePath,
|
|
178
|
+
configBase,
|
|
179
|
+
});
|
|
164
180
|
try {
|
|
165
181
|
const config = await parseConfig(configPath);
|
|
166
182
|
const configA = mergeAndNormalizeConfig({
|
|
@@ -170,8 +186,9 @@ const getFullConfig = async function ({ configOpt, cwd, context, repositoryRoot,
|
|
|
170
186
|
context,
|
|
171
187
|
branch,
|
|
172
188
|
logs,
|
|
189
|
+
packagePath,
|
|
173
190
|
});
|
|
174
|
-
const { config: configB, buildDir, base: baseA, } = await resolveFiles({ config: configA, repositoryRoot, base, baseRelDir });
|
|
191
|
+
const { config: configB, buildDir, base: baseA, } = await resolveFiles({ packagePath, config: configA, repositoryRoot, base, baseRelDir });
|
|
175
192
|
const headersPath = getHeadersPath(configB);
|
|
176
193
|
const configC = await addHeaders({ config: configB, headersPath, logs, featureFlags });
|
|
177
194
|
const redirectsPath = getRedirectsPath(configC);
|
|
@@ -184,33 +201,36 @@ const getFullConfig = async function ({ configOpt, cwd, context, repositoryRoot,
|
|
|
184
201
|
throw error;
|
|
185
202
|
}
|
|
186
203
|
};
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
204
|
+
/**
|
|
205
|
+
* Merge:
|
|
206
|
+
* - `--defaultConfig`: UI build settings and UI-installed plugins
|
|
207
|
+
* - `inlineConfig`: Netlify CLI flags
|
|
208
|
+
* Then merge context-specific configuration.
|
|
209
|
+
* Before and after those steps, also performs validation and normalization.
|
|
210
|
+
* Those need to be done at different stages depending on whether they should
|
|
211
|
+
* happen before/after the merges mentioned above.
|
|
212
|
+
*/
|
|
213
|
+
const mergeAndNormalizeConfig = function ({ config, defaultConfig, inlineConfig, context, branch, logs, packagePath }) {
|
|
195
214
|
const configA = normalizeConfigAndContext(config, CONFIG_ORIGIN);
|
|
196
215
|
const defaultConfigA = normalizeConfigAndContext(defaultConfig, UI_ORIGIN);
|
|
197
216
|
const inlineConfigA = normalizeConfigAndContext(inlineConfig, INLINE_ORIGIN);
|
|
198
217
|
const configB = mergeConfigs([defaultConfigA, configA]);
|
|
199
218
|
const configC = mergeContext({ config: configB, context, branch, logs });
|
|
200
219
|
const configD = mergeConfigs([configC, inlineConfigA]);
|
|
201
|
-
|
|
202
|
-
return configE;
|
|
220
|
+
return normalizeAfterConfigMerge(configD, packagePath);
|
|
203
221
|
};
|
|
204
222
|
const normalizeConfigAndContext = function (config, origin) {
|
|
205
223
|
const configA = normalizeBeforeConfigMerge(config, origin);
|
|
206
224
|
const configB = normalizeContextProps({ config: configA, origin });
|
|
207
225
|
return configB;
|
|
208
226
|
};
|
|
209
|
-
|
|
210
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Find base directory, build directory and resolve all paths to absolute paths
|
|
229
|
+
*/
|
|
230
|
+
const resolveFiles = async function ({ config, repositoryRoot, base, packagePath, baseRelDir, }) {
|
|
211
231
|
const baseA = getBase(base, repositoryRoot, config);
|
|
212
232
|
const buildDir = await getBuildDir(repositoryRoot, baseA);
|
|
213
|
-
const configA = resolveConfigPaths({ config, repositoryRoot, buildDir, baseRelDir });
|
|
233
|
+
const configA = resolveConfigPaths({ config, packagePath, repositoryRoot, buildDir, baseRelDir });
|
|
214
234
|
const configB = addBase(configA, baseA);
|
|
215
235
|
return { config: configB, buildDir, base: baseA };
|
|
216
236
|
};
|
package/lib/merge_normalize.js
CHANGED
|
@@ -3,10 +3,12 @@ import { normalizeConfig } from './normalize.js';
|
|
|
3
3
|
import { addOrigins } from './origin.js';
|
|
4
4
|
import { validateIdenticalPlugins } from './validate/identical.js';
|
|
5
5
|
import { validatePreCaseNormalize, validatePreMergeConfig, validatePreNormalizeConfig, validatePostNormalizeConfig, } from './validate/main.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Perform validation and normalization logic to apply to all of:
|
|
8
|
+
* - config, defaultConfig, inlineConfig
|
|
9
|
+
* - context-specific configs
|
|
10
|
+
* Therefore, this is performing before merging those together.
|
|
11
|
+
*/
|
|
10
12
|
export const normalizeBeforeConfigMerge = function (config, origin) {
|
|
11
13
|
validatePreCaseNormalize(config);
|
|
12
14
|
const configA = normalizeConfigCase(config);
|
|
@@ -15,10 +17,12 @@ export const normalizeBeforeConfigMerge = function (config, origin) {
|
|
|
15
17
|
validateIdenticalPlugins(configB);
|
|
16
18
|
return configB;
|
|
17
19
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Validation and normalization logic performed after merging
|
|
22
|
+
*/
|
|
23
|
+
export const normalizeAfterConfigMerge = function (config, packagePath) {
|
|
20
24
|
validatePreNormalizeConfig(config);
|
|
21
|
-
const configA = normalizeConfig(config);
|
|
25
|
+
const configA = normalizeConfig(config, packagePath);
|
|
22
26
|
validatePostNormalizeConfig(configA);
|
|
23
27
|
return configA;
|
|
24
28
|
};
|
package/lib/normalize.js
CHANGED
|
@@ -2,31 +2,35 @@ import { normalizeFunctionsProps, WILDCARD_ALL } from './functions_config.js';
|
|
|
2
2
|
import { mergeConfigs } from './merge.js';
|
|
3
3
|
import { DEFAULT_ORIGIN } from './origin.js';
|
|
4
4
|
import { removeFalsy } from './utils/remove_falsy.js';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Normalize configuration object
|
|
7
|
+
*/
|
|
8
|
+
export const normalizeConfig = function (config, packagePath) {
|
|
7
9
|
const configA = removeEmpty(config);
|
|
8
|
-
const { build, functions, plugins, ...configB } = mergeConfigs([DEFAULT_CONFIG, configA]);
|
|
10
|
+
const { build, functions, plugins, ...configB } = mergeConfigs([DEFAULT_CONFIG(packagePath), configA]);
|
|
9
11
|
const { build: buildA, functions: functionsA, functionsDirectoryProps } = normalizeFunctionsProps(build, functions);
|
|
10
12
|
const pluginsA = plugins.map(normalizePlugin);
|
|
11
13
|
return { ...configB, build: buildA, functions: functionsA, plugins: pluginsA, ...functionsDirectoryProps };
|
|
12
14
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Remove empty strings.
|
|
17
|
+
* This notably ensures that empty strings in the build command are removed.
|
|
18
|
+
* Otherwise, those would be run during builds, making the build fail.
|
|
19
|
+
*/
|
|
16
20
|
const removeEmpty = function ({ build, ...config }) {
|
|
17
21
|
return removeFalsy({ ...config, build: removeFalsy(build) });
|
|
18
22
|
};
|
|
19
|
-
const DEFAULT_CONFIG = {
|
|
23
|
+
const DEFAULT_CONFIG = (packagePath) => ({
|
|
20
24
|
build: {
|
|
21
25
|
environment: {},
|
|
22
|
-
publish: '.',
|
|
26
|
+
publish: packagePath || '.',
|
|
23
27
|
publishOrigin: DEFAULT_ORIGIN,
|
|
24
28
|
processing: { css: {}, html: {}, images: {}, js: {} },
|
|
25
29
|
services: {},
|
|
26
30
|
},
|
|
27
31
|
functions: { [WILDCARD_ALL]: {} },
|
|
28
32
|
plugins: [],
|
|
29
|
-
};
|
|
33
|
+
});
|
|
30
34
|
const normalizePlugin = function ({ inputs = {}, ...plugin }) {
|
|
31
35
|
return removeFalsy({ ...plugin, inputs });
|
|
32
36
|
};
|
package/lib/path.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
|
-
import { resolve } from 'path';
|
|
2
|
+
import { join, resolve } from 'path';
|
|
3
3
|
import { findUp } from 'find-up';
|
|
4
4
|
import pLocate from 'p-locate';
|
|
5
|
+
const FILENAME = 'netlify.toml';
|
|
5
6
|
/**
|
|
6
7
|
* Configuration location can be:
|
|
7
8
|
* - a local path with the --config CLI flag
|
|
@@ -9,10 +10,10 @@ import pLocate from 'p-locate';
|
|
|
9
10
|
* - a `netlify.*` file in the `repositoryRoot`
|
|
10
11
|
* - a `netlify.*` file in the current directory or any parent
|
|
11
12
|
*/
|
|
12
|
-
export const getConfigPath = async function ({ configOpt, cwd, repositoryRoot, configBase }) {
|
|
13
|
+
export const getConfigPath = async function ({ configOpt, cwd, repositoryRoot, configBase, packagePath, }) {
|
|
13
14
|
const configPath = await pLocate([
|
|
14
15
|
searchConfigOpt(cwd, configOpt),
|
|
15
|
-
searchBaseConfigFile(configBase),
|
|
16
|
+
searchBaseConfigFile(repositoryRoot, configBase, packagePath),
|
|
16
17
|
searchConfigFile(repositoryRoot),
|
|
17
18
|
findUp(FILENAME, { cwd }),
|
|
18
19
|
], Boolean);
|
|
@@ -26,13 +27,13 @@ const searchConfigOpt = function (cwd, configOpt) {
|
|
|
26
27
|
return resolve(cwd, configOpt);
|
|
27
28
|
};
|
|
28
29
|
/**
|
|
29
|
-
* Look for `repositoryRoot/{base}/netlify.*`
|
|
30
|
+
* Look for `repositoryRoot/{base}/{packagePath || '}/netlify.*`
|
|
30
31
|
*/
|
|
31
|
-
const searchBaseConfigFile = function (
|
|
32
|
-
if (
|
|
32
|
+
const searchBaseConfigFile = function (repoRoot, base, packagePath) {
|
|
33
|
+
if (base === undefined && packagePath === undefined) {
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
|
-
return searchConfigFile(
|
|
36
|
+
return searchConfigFile(join(base ? base : repoRoot, packagePath || ''));
|
|
36
37
|
};
|
|
37
38
|
/**
|
|
38
39
|
* Look for several file extensions for `netlify.*`
|
|
@@ -44,4 +45,3 @@ const searchConfigFile = function (cwd) {
|
|
|
44
45
|
}
|
|
45
46
|
return path;
|
|
46
47
|
};
|
|
47
|
-
const FILENAME = 'netlify.toml';
|
package/lib/redirects.js
CHANGED
|
@@ -6,13 +6,14 @@ export const getRedirectsPath = function ({ build: { publish } }) {
|
|
|
6
6
|
return resolve(publish, REDIRECTS_FILENAME);
|
|
7
7
|
};
|
|
8
8
|
const REDIRECTS_FILENAME = '_redirects';
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Add `config.redirects`
|
|
11
|
+
*/
|
|
12
|
+
export const addRedirects = async function ({ config: { redirects: configRedirects, ...config }, redirectsPath, logs, }) {
|
|
11
13
|
const { redirects, errors } = await parseAllRedirects({
|
|
12
14
|
redirectsFiles: [redirectsPath],
|
|
13
15
|
configRedirects,
|
|
14
16
|
minimal: true,
|
|
15
|
-
featureFlags,
|
|
16
17
|
});
|
|
17
18
|
warnRedirectsParsing(logs, errors);
|
|
18
19
|
return { ...config, redirects };
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { includeKeys } from 'filter-obj';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Remove falsy values from object
|
|
4
|
+
*/
|
|
3
5
|
export const removeFalsy = function (obj) {
|
|
4
|
-
return includeKeys(obj, (
|
|
5
|
-
};
|
|
6
|
-
export const removeUndefined = function (obj) {
|
|
7
|
-
return includeKeys(obj, (key, value) => isDefined(value));
|
|
8
|
-
};
|
|
9
|
-
export const isTruthy = function (value) {
|
|
10
|
-
return isDefined(value) && (typeof value !== 'string' || value.trim() !== '');
|
|
11
|
-
};
|
|
12
|
-
export const isDefined = function (value) {
|
|
13
|
-
return value !== undefined && value !== null;
|
|
6
|
+
return includeKeys(obj, (_key, value) => isTruthy(value));
|
|
14
7
|
};
|
|
8
|
+
export const removeUndefined = (obj) => includeKeys(obj, (_key, value) => isDefined(value));
|
|
9
|
+
export const isTruthy = (value) => isDefined(value) && (typeof value !== 'string' || value.trim() !== '');
|
|
10
|
+
export const isDefined = (value) => value !== undefined && value !== null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/config",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.8.0-rc.0",
|
|
4
4
|
"description": "Netlify config module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./lib/index.js",
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"map-obj": "^5.0.0",
|
|
73
73
|
"netlify": "^13.1.10",
|
|
74
74
|
"netlify-headers-parser": "^7.1.2",
|
|
75
|
-
"netlify-redirect-parser": "^14.1.3",
|
|
76
75
|
"node-fetch": "^3.3.1",
|
|
76
|
+
"netlify-redirect-parser": "^14.1.3",
|
|
77
77
|
"omit.js": "^2.0.2",
|
|
78
78
|
"p-locate": "^6.0.0",
|
|
79
79
|
"path-type": "^5.0.0",
|
|
@@ -93,6 +93,5 @@
|
|
|
93
93
|
},
|
|
94
94
|
"engines": {
|
|
95
95
|
"node": "^14.16.0 || >=16.0.0"
|
|
96
|
-
}
|
|
97
|
-
"gitHead": "a20207ec7cdc1101a875d9411355bd48f12733a3"
|
|
96
|
+
}
|
|
98
97
|
}
|