UNPKG

4.05 kBJavaScriptView Raw
1'use strict'
2
3const { resolve, relative, parse } = require('path')
4
5const { get, set, delete: deleteProp } = require('dot-prop')
6const pathExists = require('path-exists')
7
8const { throwUserError } = require('./error')
9const { mergeConfigs } = require('./merge')
10const { isTruthy } = require('./utils/remove_falsy')
11
12// Make configuration paths relative to `buildDir` and converts them to
13// absolute paths
14const resolveConfigPaths = async function ({ config, repositoryRoot, buildDir, baseRelDir }) {
15 const baseRel = baseRelDir ? buildDir : repositoryRoot
16 const configA = resolvePaths(config, FILE_PATH_CONFIG_PROPS, baseRel, repositoryRoot)
17 const configB = await addDefaultPaths(configA, repositoryRoot, baseRel)
18 return configB
19}
20
21// All file paths in the configuration file are are relative to `buildDir`
22// (if `baseRelDir` is `true`).
23const FILE_PATH_CONFIG_PROPS = ['functionsDirectory', 'build.publish', 'build.edge_handlers']
24
25const resolvePaths = function (config, propNames, baseRel, repositoryRoot) {
26 return propNames.reduce((configA, propName) => resolvePathProp(configA, propName, baseRel, repositoryRoot), config)
27}
28
29const resolvePathProp = function (config, propName, baseRel, repositoryRoot) {
30 const path = get(config, propName)
31
32 if (!isTruthy(path)) {
33 deleteProp(config, propName)
34 return config
35 }
36
37 return set(config, propName, resolvePath(repositoryRoot, baseRel, path, propName))
38}
39
40const resolvePath = function (repositoryRoot, baseRel, originalPath, propName) {
41 if (!isTruthy(originalPath)) {
42 return
43 }
44
45 const path = originalPath.replace(LEADING_SLASH_REGEXP, '')
46 const pathA = resolve(baseRel, path)
47 validateInsideRoot(originalPath, pathA, repositoryRoot, propName)
48 return pathA
49}
50
51// We allow paths in configuration file to start with /
52// In that case, those are actually relative paths not absolute.
53const LEADING_SLASH_REGEXP = /^\/+/
54
55// We ensure all file paths are within the repository root directory.
56// However we allow file paths to be outside of the build directory, since this
57// can be convenient in monorepo setups.
58const validateInsideRoot = function (originalPath, path, repositoryRoot, propName) {
59 if (relative(repositoryRoot, path).startsWith('..') || getWindowsDrive(repositoryRoot) !== getWindowsDrive(path)) {
60 throwUserError(
61 `Configuration property "${propName}" "${originalPath}" must be inside the repository root directory.`,
62 )
63 }
64}
65
66const getWindowsDrive = function (path) {
67 return parse(path).root
68}
69
70// Some configuration properties have default values that are only set if a
71// specific directory/file exists in the build directory
72const addDefaultPaths = async function (config, repositoryRoot, baseRel) {
73 const defaultPathsConfigs = await Promise.all(
74 DEFAULT_PATHS.map(({ defaultPath, getConfig, propName }) =>
75 addDefaultPath({ repositoryRoot, baseRel, defaultPath, getConfig, propName }),
76 ),
77 )
78 const defaultPathsConfigsA = defaultPathsConfigs.filter(Boolean)
79 return mergeConfigs([...defaultPathsConfigsA, config])
80}
81
82const DEFAULT_PATHS = [
83 // @todo Remove once we drop support for the legacy default functions directory.
84 {
85 getConfig: (directory) => ({ functionsDirectory: directory, functionsDirectoryOrigin: 'default-v1' }),
86 defaultPath: 'netlify-automatic-functions',
87 propName: 'functions.directory',
88 },
89 {
90 getConfig: (directory) => ({ functionsDirectory: directory, functionsDirectoryOrigin: 'default' }),
91 defaultPath: 'netlify/functions',
92 propName: 'functions.directory',
93 },
94 {
95 getConfig: (directory) => ({ build: { edge_handlers: directory } }),
96 defaultPath: 'netlify/edge-handlers',
97 propName: 'build.edge_handlers',
98 },
99]
100
101const addDefaultPath = async function ({ repositoryRoot, baseRel, defaultPath, getConfig, propName }) {
102 const absolutePath = resolvePath(repositoryRoot, baseRel, defaultPath, propName)
103
104 if (!(await pathExists(absolutePath))) {
105 return
106 }
107
108 return getConfig(absolutePath)
109}
110
111module.exports = { resolveConfigPaths, resolvePath }