kempo-server 1.7.0 → 1.7.2
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/README.md +21 -10
- package/package.json +1 -1
- package/src/defaultConfig.js +2 -2
- package/src/router.js +75 -33
- package/tests/config-flag.node-test.js +51 -0
- package/tests/customRoute-outside-root.node-test.js +81 -0
- package/tests/router-wildcard-double-asterisk.node-test.js +3 -3
- package/tests/router-wildcard.node-test.js +3 -3
package/README.md
CHANGED
|
@@ -213,6 +213,11 @@ export default async function(request, response) {
|
|
|
213
213
|
|
|
214
214
|
To configure the server, create a configuration file (by default `.config.json`) within the root directory of your server (`public` in the start example [above](#getting-started)). You can specify a different configuration file using the `--config` flag.
|
|
215
215
|
|
|
216
|
+
**Important:**
|
|
217
|
+
- When using a relative path for the `--config` flag, the config file must be located within the server root directory
|
|
218
|
+
- When using an absolute path for the `--config` flag, the config file can be located anywhere on the filesystem
|
|
219
|
+
- The server will throw an error if you attempt to use a relative config file path that points outside the root directory
|
|
220
|
+
|
|
216
221
|
This json file can have any of the following properties, any property not defined will use the "Default Config".
|
|
217
222
|
|
|
218
223
|
- [allowedMimes](#allowedmimes)
|
|
@@ -558,35 +563,41 @@ An array of regex patterns for paths that should not trigger a file system resca
|
|
|
558
563
|
|
|
559
564
|
An object mapping custom route paths to file paths. Useful for aliasing or serving files from outside the document root.
|
|
560
565
|
|
|
566
|
+
**Note:** All file paths in customRoutes are resolved relative to the server root directory (the `--root` path). This allows you to reference files both inside and outside the document root.
|
|
567
|
+
|
|
561
568
|
**Basic Routes:**
|
|
562
569
|
```json
|
|
563
570
|
{
|
|
564
571
|
"customRoutes": {
|
|
565
|
-
"/vendor/bootstrap.css": "
|
|
572
|
+
"/vendor/bootstrap.css": "../node_modules/bootstrap/dist/css/bootstrap.min.css",
|
|
566
573
|
"/api/status": "./status.js"
|
|
567
574
|
}
|
|
568
575
|
}
|
|
569
576
|
```
|
|
570
577
|
|
|
571
578
|
**Wildcard Routes:**
|
|
572
|
-
Wildcard routes allow you to map entire directory structures using the `*`
|
|
579
|
+
Wildcard routes allow you to map entire directory structures using the `*` and `**` wildcards:
|
|
573
580
|
|
|
574
581
|
```json
|
|
575
582
|
{
|
|
576
583
|
"customRoutes": {
|
|
577
|
-
"kempo/*": "
|
|
578
|
-
"assets/*": "
|
|
579
|
-
"docs/*": "
|
|
584
|
+
"kempo/*": "../node_modules/kempo/dist/*",
|
|
585
|
+
"assets/*": "../static-files/*",
|
|
586
|
+
"docs/*": "../documentation/*",
|
|
587
|
+
"src/**": "../src/**"
|
|
580
588
|
}
|
|
581
589
|
}
|
|
582
590
|
```
|
|
583
591
|
|
|
584
592
|
With wildcard routes:
|
|
585
|
-
- `kempo/styles.css` would serve
|
|
586
|
-
- `assets/logo.png` would serve
|
|
587
|
-
- `docs/readme.md` would serve
|
|
588
|
-
|
|
589
|
-
|
|
593
|
+
- `kempo/styles.css` would serve `../node_modules/kempo/dist/styles.css`
|
|
594
|
+
- `assets/logo.png` would serve `../static-files/logo.png`
|
|
595
|
+
- `docs/readme.md` would serve `../documentation/readme.md`
|
|
596
|
+
- `src/components/Button.js` would serve `../src/components/Button.js`
|
|
597
|
+
|
|
598
|
+
The `*` wildcard matches any single path segment (anything between `/` characters).
|
|
599
|
+
The `**` wildcard matches any number of path segments, allowing you to map entire directory trees.
|
|
600
|
+
Multiple wildcards can be used in a single route pattern.
|
|
590
601
|
|
|
591
602
|
### maxRescanAttempts
|
|
592
603
|
|
package/package.json
CHANGED
package/src/defaultConfig.js
CHANGED
|
@@ -88,8 +88,8 @@ export default {
|
|
|
88
88
|
],
|
|
89
89
|
maxRescanAttempts: 3,
|
|
90
90
|
customRoutes: {
|
|
91
|
-
// Example: "/vendor/bootstrap.css": "
|
|
92
|
-
// Wildcard example: "kempo/*": "
|
|
91
|
+
// Example: "/vendor/bootstrap.css": "../node_modules/bootstrap/dist/css/bootstrap.min.css"
|
|
92
|
+
// Wildcard example: "kempo/*": "../node_modules/kempo/dust/*"
|
|
93
93
|
},
|
|
94
94
|
middleware: {
|
|
95
95
|
// Built-in middleware configuration
|
package/src/router.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
|
|
18
18
|
export default async (flags, log) => {
|
|
19
19
|
log('Initializing router', 2);
|
|
20
|
-
const rootPath = path.join(process.cwd(), flags.root);
|
|
20
|
+
const rootPath = path.isAbsolute(flags.root) ? flags.root : path.join(process.cwd(), flags.root);
|
|
21
21
|
log(`Root path: ${rootPath}`, 2);
|
|
22
22
|
|
|
23
23
|
let config = defaultConfig;
|
|
@@ -27,6 +27,25 @@ export default async (flags, log) => {
|
|
|
27
27
|
const configPath = path.isAbsolute(configFileName)
|
|
28
28
|
? configFileName
|
|
29
29
|
: path.join(rootPath, configFileName);
|
|
30
|
+
|
|
31
|
+
log(`Config file name: ${configFileName}`, 2);
|
|
32
|
+
log(`Config path: ${configPath}`, 2);
|
|
33
|
+
|
|
34
|
+
// Validate that config file is within the server root directory
|
|
35
|
+
// Allow absolute paths (user explicitly specified location)
|
|
36
|
+
if (!path.isAbsolute(configFileName)) {
|
|
37
|
+
const relativeConfigPath = path.relative(rootPath, configPath);
|
|
38
|
+
log(`Relative config path: ${relativeConfigPath}`, 2);
|
|
39
|
+
log(`Starts with '..': ${relativeConfigPath.startsWith('..')}`, 2);
|
|
40
|
+
if (relativeConfigPath.startsWith('..') || path.isAbsolute(relativeConfigPath)) {
|
|
41
|
+
log(`Validation failed - throwing error`, 2);
|
|
42
|
+
throw new Error(`Config file must be within the server root directory. Config path: ${configPath}, Root path: ${rootPath}`);
|
|
43
|
+
}
|
|
44
|
+
log(`Validation passed`, 2);
|
|
45
|
+
} else {
|
|
46
|
+
log(`Config file name is absolute, skipping validation`, 2);
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
log(`Loading config from: ${configPath}`, 2);
|
|
31
50
|
const configContent = await readFile(configPath, 'utf8');
|
|
32
51
|
const userConfig = JSON.parse(configContent);
|
|
@@ -53,6 +72,11 @@ export default async (flags, log) => {
|
|
|
53
72
|
};
|
|
54
73
|
log('User config loaded and merged with defaults', 2);
|
|
55
74
|
} catch (e){
|
|
75
|
+
// Only fall back to default config for file reading/parsing errors
|
|
76
|
+
// Let validation errors propagate up
|
|
77
|
+
if (e.message.includes('Config file must be within the server root directory')) {
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
56
80
|
log('Using default config (no config file found)', 2);
|
|
57
81
|
}
|
|
58
82
|
|
|
@@ -136,27 +160,19 @@ export default async (flags, log) => {
|
|
|
136
160
|
|
|
137
161
|
if (config.customRoutes && Object.keys(config.customRoutes).length > 0) {
|
|
138
162
|
log(`Processing ${Object.keys(config.customRoutes).length} custom routes`, 2);
|
|
139
|
-
|
|
140
163
|
for (const [urlPath, filePath] of Object.entries(config.customRoutes)) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
await stat(resolvedPath);
|
|
154
|
-
|
|
155
|
-
customRoutes.set(urlPath, resolvedPath);
|
|
156
|
-
log(`Custom route mapped: ${urlPath} -> ${resolvedPath}`, 2);
|
|
157
|
-
}
|
|
158
|
-
} catch (error) {
|
|
159
|
-
log(`Custom route error for ${urlPath} -> ${filePath}: ${error.message}`, 1);
|
|
164
|
+
// Check if this is a wildcard route
|
|
165
|
+
if (urlPath.includes('*')) {
|
|
166
|
+
// Resolve the file path relative to rootPath if relative, otherwise use absolute path
|
|
167
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(rootPath, filePath);
|
|
168
|
+
// Store wildcard routes separately for pattern matching
|
|
169
|
+
wildcardRoutes.set(urlPath, resolvedPath);
|
|
170
|
+
log(`Wildcard route mapped: ${urlPath} -> ${resolvedPath}`, 2);
|
|
171
|
+
} else {
|
|
172
|
+
// Resolve the file path relative to rootPath if relative, otherwise use absolute path
|
|
173
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(rootPath, filePath);
|
|
174
|
+
customRoutes.set(urlPath, resolvedPath);
|
|
175
|
+
log(`Custom route mapped: ${urlPath} -> ${resolvedPath}`, 2);
|
|
160
176
|
}
|
|
161
177
|
}
|
|
162
178
|
}
|
|
@@ -187,7 +203,9 @@ export default async (flags, log) => {
|
|
|
187
203
|
}
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
return
|
|
206
|
+
// If the path is already absolute, return it as-is
|
|
207
|
+
// If it's relative, resolve it relative to rootPath
|
|
208
|
+
return path.isAbsolute(resolvedPath) ? resolvedPath : path.resolve(rootPath, resolvedPath);
|
|
191
209
|
};
|
|
192
210
|
|
|
193
211
|
// Helper function to find matching wildcard route
|
|
@@ -247,39 +265,63 @@ export default async (flags, log) => {
|
|
|
247
265
|
const requestPath = req.url.split('?')[0];
|
|
248
266
|
log(`${req.method} ${requestPath}`, 0);
|
|
249
267
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
268
|
+
|
|
269
|
+
// Check custom routes first (allow outside rootPath)
|
|
270
|
+
log(`customRoutes keys: ${Array.from(customRoutes.keys()).join(', ')}`, 1);
|
|
271
|
+
// Normalize requestPath and keys for matching
|
|
272
|
+
const normalizePath = p => {
|
|
273
|
+
let np = decodeURIComponent(p);
|
|
274
|
+
if (!np.startsWith('/')) np = '/' + np;
|
|
275
|
+
if (np.length > 1 && np.endsWith('/')) np = np.slice(0, -1);
|
|
276
|
+
return np;
|
|
277
|
+
};
|
|
278
|
+
const normalizedRequestPath = normalizePath(requestPath);
|
|
279
|
+
log(`Normalized requestPath: ${normalizedRequestPath}`, 1);
|
|
280
|
+
let matchedKey = null;
|
|
281
|
+
for (const key of customRoutes.keys()) {
|
|
282
|
+
if (normalizePath(key) === normalizedRequestPath) {
|
|
283
|
+
matchedKey = key;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (matchedKey) {
|
|
288
|
+
const customFilePath = customRoutes.get(matchedKey);
|
|
289
|
+
log(`Serving custom route: ${normalizedRequestPath} -> ${customFilePath}`, 2);
|
|
255
290
|
try {
|
|
291
|
+
const { stat } = await import('fs/promises');
|
|
292
|
+
try {
|
|
293
|
+
await stat(customFilePath);
|
|
294
|
+
log(`Custom route file exists: ${customFilePath}`, 2);
|
|
295
|
+
} catch (e) {
|
|
296
|
+
log(`Custom route file does NOT exist: ${customFilePath}`, 0);
|
|
297
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
298
|
+
res.end('Custom route file not found');
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
256
301
|
const fileContent = await readFile(customFilePath);
|
|
257
302
|
const fileExtension = path.extname(customFilePath).toLowerCase().slice(1);
|
|
258
303
|
const mimeType = config.allowedMimes[fileExtension] || 'application/octet-stream';
|
|
259
|
-
|
|
260
304
|
log(`Serving custom file as ${mimeType} (${fileContent.length} bytes)`, 2);
|
|
261
305
|
res.writeHead(200, { 'Content-Type': mimeType });
|
|
262
306
|
res.end(fileContent);
|
|
263
307
|
return; // Successfully served custom route
|
|
264
308
|
} catch (error) {
|
|
265
|
-
log(`Error serving custom route ${
|
|
309
|
+
log(`Error serving custom route ${normalizedRequestPath}: ${error.message}`, 0);
|
|
266
310
|
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
267
311
|
res.end('Internal Server Error');
|
|
268
312
|
return;
|
|
269
313
|
}
|
|
270
314
|
}
|
|
271
|
-
|
|
272
|
-
// Check wildcard routes
|
|
315
|
+
|
|
316
|
+
// Check wildcard routes (allow outside rootPath)
|
|
273
317
|
const wildcardMatch = findWildcardRoute(requestPath);
|
|
274
318
|
if (wildcardMatch) {
|
|
275
319
|
const resolvedFilePath = resolveWildcardPath(wildcardMatch.filePath, wildcardMatch.matches);
|
|
276
320
|
log(`Serving wildcard route: ${requestPath} -> ${resolvedFilePath}`, 2);
|
|
277
|
-
|
|
278
321
|
try {
|
|
279
322
|
const fileContent = await readFile(resolvedFilePath);
|
|
280
323
|
const fileExtension = path.extname(resolvedFilePath).toLowerCase().slice(1);
|
|
281
324
|
const mimeType = config.allowedMimes[fileExtension] || 'application/octet-stream';
|
|
282
|
-
|
|
283
325
|
log(`Serving wildcard file as ${mimeType} (${fileContent.length} bytes)`, 2);
|
|
284
326
|
res.writeHead(200, { 'Content-Type': mimeType });
|
|
285
327
|
res.end(fileContent);
|
|
@@ -317,5 +317,56 @@ export default {
|
|
|
317
317
|
process.chdir(prev);
|
|
318
318
|
}
|
|
319
319
|
});
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
'router throws error when config file is outside server root': async ({pass, fail, log}) => {
|
|
323
|
+
await withTempDir(async (dir) => {
|
|
324
|
+
// Create a config file outside the server root
|
|
325
|
+
const configDir = path.join(dir, '..', 'config-outside-root');
|
|
326
|
+
const configFilePath = await write(configDir, 'outside.config.json', '{"allowedMimes": {"test": "application/test"}}');
|
|
327
|
+
|
|
328
|
+
// Create a file in the server root to verify it doesn't start
|
|
329
|
+
await write(dir, 'index.html', '<h1>Home</h1>');
|
|
330
|
+
|
|
331
|
+
const prev = process.cwd();
|
|
332
|
+
process.chdir(dir);
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
// Try to use config file outside server root with relative path
|
|
336
|
+
const flags = {root: '.', logging: 0, scan: false, config: '../config-outside-root/outside.config.json'};
|
|
337
|
+
|
|
338
|
+
log('Test setup:');
|
|
339
|
+
log('dir: ' + dir);
|
|
340
|
+
log('configDir: ' + configDir);
|
|
341
|
+
log('configFilePath: ' + configFilePath);
|
|
342
|
+
log('flags.root: ' + flags.root);
|
|
343
|
+
log('flags.config: ' + flags.config);
|
|
344
|
+
|
|
345
|
+
// Check if file exists
|
|
346
|
+
const fs = await import('fs/promises');
|
|
347
|
+
try {
|
|
348
|
+
await fs.access(configFilePath);
|
|
349
|
+
log('Config file exists at: ' + configFilePath);
|
|
350
|
+
} catch (e) {
|
|
351
|
+
log('Config file does NOT exist at: ' + configFilePath);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// This should throw an error
|
|
355
|
+
await router(flags, log);
|
|
356
|
+
|
|
357
|
+
// If we reach here, the test failed
|
|
358
|
+
return fail('router should have thrown error for config file outside root');
|
|
359
|
+
} catch (error) {
|
|
360
|
+
log('Error caught: ' + error.message);
|
|
361
|
+
// Verify the error message contains expected text
|
|
362
|
+
if (!error.message.includes('Config file must be within the server root directory')) {
|
|
363
|
+
return fail(`unexpected error message: ${error.message}`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
pass('router correctly throws error for config file outside server root');
|
|
367
|
+
} finally {
|
|
368
|
+
process.chdir(prev);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
320
371
|
}
|
|
321
372
|
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import router from '../src/router.js';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { writeFile, mkdir } from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { randomPort, httpGet, withTempDir, write } from './test-utils.js';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
This test verifies that a customRoute pointing outside the rootPath is served
|
|
9
|
+
instead of a static file inside the rootPath, if both exist.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
'customRoute outside rootPath takes precedence over static file': async ({pass, fail, log}) => {
|
|
14
|
+
try {
|
|
15
|
+
await withTempDir(async (dir) => {
|
|
16
|
+
// Print initial working directory
|
|
17
|
+
log('Initial process.cwd(): ' + process.cwd());
|
|
18
|
+
|
|
19
|
+
// Setup: create static file in rootPath
|
|
20
|
+
const rootDir = path.join(dir, 'public');
|
|
21
|
+
await mkdir(path.join(rootDir, 'src'), { recursive: true });
|
|
22
|
+
await writeFile(path.join(rootDir, 'src', 'file.txt'), 'static');
|
|
23
|
+
// Create custom file outside rootPath, matching resolved customRoute
|
|
24
|
+
const customFilePath = path.resolve(rootDir, '..', 'src', 'file.txt');
|
|
25
|
+
await mkdir(path.dirname(customFilePath), { recursive: true });
|
|
26
|
+
await writeFile(customFilePath, 'custom');
|
|
27
|
+
log('Custom file path: ' + customFilePath);
|
|
28
|
+
|
|
29
|
+
// Check file existence
|
|
30
|
+
try {
|
|
31
|
+
const { stat } = await import('fs/promises');
|
|
32
|
+
await stat(customFilePath);
|
|
33
|
+
log('Custom file exists at setup');
|
|
34
|
+
} catch (e) {
|
|
35
|
+
log('Custom file does NOT exist at setup');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Write config with customRoute pointing outside rootPath
|
|
39
|
+
const config = {
|
|
40
|
+
customRoutes: {
|
|
41
|
+
'/src/file.txt': '../src/file.txt'
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
await writeFile(path.join(rootDir, '.config.json'), JSON.stringify(config));
|
|
45
|
+
log('Config written: ' + JSON.stringify(config));
|
|
46
|
+
|
|
47
|
+
// Set working directory to temp dir so relative paths resolve correctly
|
|
48
|
+
const prevCwd = process.cwd();
|
|
49
|
+
process.chdir(dir);
|
|
50
|
+
const flags = { root: rootDir, logging: 4 };
|
|
51
|
+
const logFn = (...args) => log(args.map(String).join(' '));
|
|
52
|
+
log('Starting server with flags: ' + JSON.stringify(flags));
|
|
53
|
+
log('process.cwd() before router: ' + process.cwd());
|
|
54
|
+
const handler = await router(flags, logFn);
|
|
55
|
+
const server = http.createServer(handler);
|
|
56
|
+
const port = randomPort();
|
|
57
|
+
await new Promise(r => server.listen(port, r));
|
|
58
|
+
await new Promise(r => setTimeout(r, 50));
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
// Print process.cwd() before request
|
|
62
|
+
log('process.cwd() before request: ' + process.cwd());
|
|
63
|
+
// Request the file
|
|
64
|
+
const requestUrl = `http://localhost:${port}/src/file.txt`;
|
|
65
|
+
log('Requesting URL: ' + requestUrl);
|
|
66
|
+
const response = await httpGet(requestUrl);
|
|
67
|
+
log('status: ' + response.res.statusCode);
|
|
68
|
+
log('response body: ' + response.body.toString());
|
|
69
|
+
if(response.res.statusCode !== 200) throw new Error('status not 200');
|
|
70
|
+
if(response.body.toString() !== 'custom') throw new Error('did not serve customRoute file');
|
|
71
|
+
} finally {
|
|
72
|
+
server.close();
|
|
73
|
+
process.chdir(prevCwd);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
pass('customRoute outside rootPath is served');
|
|
77
|
+
} catch(e){
|
|
78
|
+
fail(e.message);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
// Configure double asterisk wildcard route
|
|
26
26
|
const config = {
|
|
27
27
|
customRoutes: {
|
|
28
|
-
'/src/**': '
|
|
28
|
+
'/src/**': '../src/**'
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -86,7 +86,7 @@ export default {
|
|
|
86
86
|
// Configure single asterisk wildcard route
|
|
87
87
|
const config = {
|
|
88
88
|
customRoutes: {
|
|
89
|
-
'/src/*': '
|
|
89
|
+
'/src/*': '../src/*'
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
|
|
@@ -140,7 +140,7 @@ export default {
|
|
|
140
140
|
// Configure wildcard route that overrides static file
|
|
141
141
|
const config = {
|
|
142
142
|
customRoutes: {
|
|
143
|
-
'/api/**': '
|
|
143
|
+
'/api/**': '../custom/**'
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
146
|
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
// Configure double asterisk wildcard route
|
|
26
26
|
const config = {
|
|
27
27
|
customRoutes: {
|
|
28
|
-
'/src/**': '
|
|
28
|
+
'/src/**': '../src/**'
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -86,7 +86,7 @@ export default {
|
|
|
86
86
|
// Configure single asterisk wildcard route
|
|
87
87
|
const config = {
|
|
88
88
|
customRoutes: {
|
|
89
|
-
'/src/*': '
|
|
89
|
+
'/src/*': '../src/*'
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
|
|
@@ -140,7 +140,7 @@ export default {
|
|
|
140
140
|
// Configure wildcard route that overrides static file
|
|
141
141
|
const config = {
|
|
142
142
|
customRoutes: {
|
|
143
|
-
'/api/**': '
|
|
143
|
+
'/api/**': '../custom/**'
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
146
|
|