wrangler 0.0.0-e95bea2 → 0.0.0-e95e5a0
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 +25 -14
- package/bin/wrangler.js +13 -1
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +33 -25
- package/pages/functions/buildPlugin.ts +92 -0
- package/pages/functions/buildWorker.ts +80 -4
- package/pages/functions/filepath-routing.test.ts +136 -25
- package/pages/functions/filepath-routing.ts +14 -3
- package/pages/functions/routes.ts +6 -1
- package/pages/functions/template-plugin.ts +146 -0
- package/pages/functions/template-worker.ts +21 -8
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +1179 -271
- package/src/__tests__/dev.test.tsx +382 -25
- package/src/__tests__/guess-worker-format.test.ts +28 -1
- package/src/__tests__/helpers/mock-account-id.ts +4 -0
- package/src/__tests__/helpers/mock-cfetch.ts +5 -1
- package/src/__tests__/helpers/mock-console.ts +20 -3
- package/src/__tests__/helpers/mock-dialogs.ts +26 -1
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +27 -0
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/run-in-tmp.ts +10 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/https-options.test.ts +5 -2
- package/src/__tests__/index.test.ts +27 -769
- package/src/__tests__/init.test.ts +1516 -0
- package/src/__tests__/jest.setup.ts +4 -4
- package/src/__tests__/kv.test.ts +172 -126
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/pages.test.ts +311 -26
- package/src/__tests__/publish.test.ts +2946 -524
- package/src/__tests__/r2.test.ts +26 -10
- package/src/__tests__/route.test.ts +4 -4
- package/src/__tests__/secret.test.ts +55 -34
- package/src/__tests__/tail.test.ts +146 -20
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +26 -7
- package/src/bundle.ts +30 -3
- package/src/cfetch/internal.ts +9 -7
- package/src/cli.ts +5 -11
- package/src/config/README.md +7 -7
- package/src/config/config.ts +33 -78
- package/src/config/environment.ts +93 -4
- package/src/config/index.ts +15 -4
- package/src/config/validation-helpers.ts +202 -15
- package/src/config/validation.ts +530 -133
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +22 -13
- package/src/create-worker-upload-form.ts +32 -9
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +136 -75
- package/src/dev/local.tsx +196 -125
- package/src/dev/remote.tsx +25 -13
- package/src/dev/use-esbuild.ts +23 -5
- package/src/dialogs.tsx +16 -6
- package/src/entry.ts +119 -11
- package/src/environment-variables.ts +3 -1
- package/src/errors.ts +11 -0
- package/src/https-options.ts +3 -2
- package/src/index.tsx +739 -400
- package/src/inspect.ts +17 -11
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv/index.tsx → kv.ts} +8 -28
- package/src/logger.ts +66 -0
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +41 -0
- package/src/miniflare-cli/request-context.ts +40 -0
- package/src/module-collection.ts +63 -58
- package/src/open-in-browser.ts +5 -4
- package/src/package-manager.ts +9 -8
- package/src/pages.tsx +883 -62
- package/src/parse.ts +1 -1
- package/src/proxy.ts +32 -21
- package/src/publish.ts +276 -161
- package/src/sites.tsx +88 -33
- package/src/tail/index.ts +2 -2
- package/src/tail/printing.ts +13 -8
- package/src/update-check.ts +19 -0
- package/src/user.tsx +231 -103
- package/src/whoami.tsx +2 -1
- package/src/worker.ts +15 -6
- package/templates/gitignore +171 -0
- package/templates/static-asset-facade.js +1 -1
- package/wrangler-dist/cli.js +22087 -49340
- package/src/__tests__/dev2.test.tsx +0 -152
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/logout.test.ts +0 -50
- package/src/__tests__/sentry.test.ts +0 -146
- package/src/reporting.ts +0 -131
- package/wrangler-dist/cli.js.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wrangler",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-e95e5a0",
|
|
4
4
|
"author": "wrangler@cloudflare.com",
|
|
5
5
|
"description": "Command-line interface for all things Cloudflare Workers",
|
|
6
6
|
"bin": {
|
|
@@ -36,10 +36,14 @@
|
|
|
36
36
|
"cli"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"esbuild": "0.
|
|
40
|
-
"
|
|
39
|
+
"@esbuild-plugins/node-globals-polyfill": "^0.1.1",
|
|
40
|
+
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
|
41
|
+
"blake3-wasm": "^2.1.5",
|
|
42
|
+
"esbuild": "0.14.34",
|
|
43
|
+
"miniflare": "2.4.0",
|
|
44
|
+
"nanoid": "^3.3.3",
|
|
41
45
|
"path-to-regexp": "^6.2.0",
|
|
42
|
-
"selfsigned": "^2.0.
|
|
46
|
+
"selfsigned": "^2.0.1",
|
|
43
47
|
"semiver": "^1.1.0",
|
|
44
48
|
"xxhash-wasm": "^1.0.1"
|
|
45
49
|
},
|
|
@@ -47,68 +51,72 @@
|
|
|
47
51
|
"fsevents": "~2.3.2"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
|
-
"@iarna/toml": "^
|
|
51
|
-
"@sentry/cli": "^1.71.0",
|
|
52
|
-
"@sentry/integrations": "^6.17.6",
|
|
53
|
-
"@sentry/node": "^6.17.6",
|
|
54
|
+
"@iarna/toml": "^3.0.0",
|
|
54
55
|
"@types/command-exists": "^1.2.0",
|
|
55
56
|
"@types/glob-to-regexp": "0.4.1",
|
|
56
57
|
"@types/mime": "^2.0.3",
|
|
57
58
|
"@types/prompts": "^2.0.14",
|
|
58
59
|
"@types/react": "^17.0.37",
|
|
59
|
-
"@types/react-test-renderer": "^17.0.1",
|
|
60
60
|
"@types/serve-static": "^1.13.10",
|
|
61
61
|
"@types/signal-exit": "^3.0.1",
|
|
62
|
-
"@types/
|
|
63
|
-
"@types/
|
|
64
|
-
"
|
|
62
|
+
"@types/supports-color": "^8.1.1",
|
|
63
|
+
"@types/ws": "^8.5.3",
|
|
64
|
+
"@types/yargs": "^17.0.10",
|
|
65
|
+
"chokidar": "^3.5.3",
|
|
65
66
|
"clipboardy": "^3.0.0",
|
|
66
67
|
"cmd-shim": "^4.1.0",
|
|
67
68
|
"command-exists": "^1.2.9",
|
|
68
69
|
"devtools-protocol": "^0.0.955664",
|
|
69
|
-
"
|
|
70
|
+
"dotenv": "^16.0.0",
|
|
71
|
+
"execa": "^6.1.0",
|
|
70
72
|
"faye-websocket": "^0.11.4",
|
|
71
|
-
"finalhandler": "^1.
|
|
72
|
-
"find-up": "^6.
|
|
73
|
+
"finalhandler": "^1.2.0",
|
|
74
|
+
"find-up": "^6.3.0",
|
|
73
75
|
"get-port": "^6.1.2",
|
|
74
76
|
"glob-to-regexp": "0.4.1",
|
|
75
77
|
"ignore": "^5.2.0",
|
|
76
78
|
"ink": "^3.2.0",
|
|
77
79
|
"ink-select-input": "^4.2.1",
|
|
80
|
+
"ink-spinner": "^4.0.3",
|
|
78
81
|
"ink-table": "^3.0.0",
|
|
79
82
|
"ink-testing-library": "^2.1.0",
|
|
80
|
-
"ink-text-input": "^4.0.
|
|
83
|
+
"ink-text-input": "^4.0.3",
|
|
81
84
|
"jest-fetch-mock": "^3.0.3",
|
|
82
85
|
"jest-websocket-mock": "^2.3.0",
|
|
83
86
|
"mime": "^3.0.0",
|
|
84
87
|
"open": "^8.4.0",
|
|
88
|
+
"pretty-bytes": "^6.0.0",
|
|
85
89
|
"prompts": "^2.4.2",
|
|
86
90
|
"react": "^17.0.2",
|
|
87
|
-
"react-test-renderer": "^17.0.2",
|
|
88
91
|
"react-error-boundary": "^3.1.4",
|
|
89
|
-
"serve-static": "^1.
|
|
90
|
-
"signal-exit": "^3.0.
|
|
92
|
+
"serve-static": "^1.15.0",
|
|
93
|
+
"signal-exit": "^3.0.7",
|
|
94
|
+
"supports-color": "^9.2.2",
|
|
95
|
+
"timeago.js": "^4.0.2",
|
|
91
96
|
"tmp-promise": "^3.0.3",
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
97
|
+
"ts-dedent": "^2.2.0",
|
|
98
|
+
"undici": "^4.15.1",
|
|
99
|
+
"update-check": "^1.5.4",
|
|
100
|
+
"ws": "^8.5.0",
|
|
101
|
+
"yargs": "^17.4.1"
|
|
95
102
|
},
|
|
96
103
|
"files": [
|
|
97
104
|
"src",
|
|
98
105
|
"bin",
|
|
99
106
|
"pages",
|
|
100
107
|
"miniflare-config-stubs",
|
|
108
|
+
"miniflare-dist",
|
|
101
109
|
"wrangler-dist",
|
|
102
110
|
"templates",
|
|
103
111
|
"vendor",
|
|
104
112
|
"import_meta_url.js"
|
|
105
113
|
],
|
|
106
114
|
"scripts": {
|
|
107
|
-
"clean": "rm -rf wrangler-dist",
|
|
115
|
+
"clean": "rm -rf wrangler-dist miniflare-dist",
|
|
108
116
|
"check:type": "tsc",
|
|
109
117
|
"bundle": "node -r esbuild-register scripts/bundle.ts",
|
|
110
118
|
"build": "npm run clean && npm run bundle",
|
|
111
|
-
"prepublishOnly": "npm run build",
|
|
119
|
+
"prepublishOnly": "SOURCEMAPS=false npm run build",
|
|
112
120
|
"start": "npm run bundle && NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
|
|
113
121
|
"test": "jest --silent=false --verbose=true",
|
|
114
122
|
"test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch"
|
|
@@ -121,7 +129,7 @@
|
|
|
121
129
|
"testTimeout": 30000,
|
|
122
130
|
"testRegex": ".*.(test|spec)\\.[jt]sx?$",
|
|
123
131
|
"transformIgnorePatterns": [
|
|
124
|
-
"node_modules/(?!find-up|locate-path|p-locate|p-limit|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port)"
|
|
132
|
+
"node_modules/(?!find-up|locate-path|p-locate|p-limit|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port|supports-color|pretty-bytes)"
|
|
125
133
|
],
|
|
126
134
|
"moduleNameMapper": {
|
|
127
135
|
"clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { access, lstat } from "node:fs/promises";
|
|
2
|
+
import { dirname, relative, resolve } from "node:path";
|
|
3
|
+
import { build } from "esbuild";
|
|
4
|
+
|
|
5
|
+
type Options = {
|
|
6
|
+
routesModule: string;
|
|
7
|
+
outfile: string;
|
|
8
|
+
minify?: boolean;
|
|
9
|
+
sourcemap?: boolean;
|
|
10
|
+
watch?: boolean;
|
|
11
|
+
onEnd?: () => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function buildPlugin({
|
|
15
|
+
routesModule,
|
|
16
|
+
outfile = "bundle.js",
|
|
17
|
+
minify = false,
|
|
18
|
+
sourcemap = false,
|
|
19
|
+
watch = false,
|
|
20
|
+
onEnd = () => {},
|
|
21
|
+
}: Options) {
|
|
22
|
+
return build({
|
|
23
|
+
entryPoints: [resolve(__dirname, "../pages/functions/template-plugin.ts")],
|
|
24
|
+
inject: [routesModule],
|
|
25
|
+
bundle: true,
|
|
26
|
+
format: "esm",
|
|
27
|
+
target: "esnext",
|
|
28
|
+
outfile,
|
|
29
|
+
minify,
|
|
30
|
+
sourcemap,
|
|
31
|
+
watch,
|
|
32
|
+
allowOverwrite: true,
|
|
33
|
+
plugins: [
|
|
34
|
+
{
|
|
35
|
+
name: "wrangler notifier and monitor",
|
|
36
|
+
setup(pluginBuild) {
|
|
37
|
+
pluginBuild.onEnd((result) => {
|
|
38
|
+
if (result.errors.length > 0) {
|
|
39
|
+
console.error(
|
|
40
|
+
`${result.errors.length} error(s) and ${result.warnings.length} warning(s) when compiling Worker.`
|
|
41
|
+
);
|
|
42
|
+
} else if (result.warnings.length > 0) {
|
|
43
|
+
console.warn(
|
|
44
|
+
`${result.warnings.length} warning(s) when compiling Worker.`
|
|
45
|
+
);
|
|
46
|
+
onEnd();
|
|
47
|
+
} else {
|
|
48
|
+
console.log("Compiled Worker successfully.");
|
|
49
|
+
onEnd();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "Assets",
|
|
56
|
+
setup(pluginBuild) {
|
|
57
|
+
if (pluginBuild.initialOptions.outfile) {
|
|
58
|
+
const outdir = dirname(pluginBuild.initialOptions.outfile);
|
|
59
|
+
|
|
60
|
+
pluginBuild.onResolve({ filter: /^assets:/ }, async (args) => {
|
|
61
|
+
const directory = resolve(
|
|
62
|
+
args.resolveDir,
|
|
63
|
+
args.path.slice("assets:".length)
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const exists = await access(directory)
|
|
67
|
+
.then(() => true)
|
|
68
|
+
.catch(() => false);
|
|
69
|
+
|
|
70
|
+
const isDirectory =
|
|
71
|
+
exists && (await lstat(directory)).isDirectory();
|
|
72
|
+
|
|
73
|
+
if (!isDirectory) {
|
|
74
|
+
return {
|
|
75
|
+
errors: [
|
|
76
|
+
{
|
|
77
|
+
text: `'${directory}' does not exist or is not a directory.`,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const path = `assets:./${relative(outdir, directory)}`;
|
|
84
|
+
|
|
85
|
+
return { path, external: true, namespace: "assets" };
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { access, cp, lstat, rm } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
2
3
|
import { build } from "esbuild";
|
|
4
|
+
import { nanoid } from "nanoid";
|
|
3
5
|
|
|
4
6
|
type Options = {
|
|
5
7
|
routesModule: string;
|
|
@@ -9,6 +11,7 @@ type Options = {
|
|
|
9
11
|
fallbackService?: string;
|
|
10
12
|
watch?: boolean;
|
|
11
13
|
onEnd?: () => void;
|
|
14
|
+
buildOutputDirectory?: string;
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
export function buildWorker({
|
|
@@ -19,11 +22,10 @@ export function buildWorker({
|
|
|
19
22
|
fallbackService = "ASSETS",
|
|
20
23
|
watch = false,
|
|
21
24
|
onEnd = () => {},
|
|
25
|
+
buildOutputDirectory,
|
|
22
26
|
}: Options) {
|
|
23
27
|
return build({
|
|
24
|
-
entryPoints: [
|
|
25
|
-
path.resolve(__dirname, "../pages/functions/template-worker.ts"),
|
|
26
|
-
],
|
|
28
|
+
entryPoints: [resolve(__dirname, "../pages/functions/template-worker.ts")],
|
|
27
29
|
inject: [routesModule],
|
|
28
30
|
bundle: true,
|
|
29
31
|
format: "esm",
|
|
@@ -57,6 +59,80 @@ export function buildWorker({
|
|
|
57
59
|
});
|
|
58
60
|
},
|
|
59
61
|
},
|
|
62
|
+
{
|
|
63
|
+
name: "Assets",
|
|
64
|
+
setup(pluginBuild) {
|
|
65
|
+
const identifiers = new Map<string, string>();
|
|
66
|
+
|
|
67
|
+
pluginBuild.onResolve({ filter: /^assets:/ }, async (args) => {
|
|
68
|
+
const directory = resolve(
|
|
69
|
+
args.resolveDir,
|
|
70
|
+
args.path.slice("assets:".length)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const exists = await access(directory)
|
|
74
|
+
.then(() => true)
|
|
75
|
+
.catch(() => false);
|
|
76
|
+
|
|
77
|
+
const isDirectory =
|
|
78
|
+
exists && (await lstat(directory)).isDirectory();
|
|
79
|
+
|
|
80
|
+
if (!isDirectory) {
|
|
81
|
+
return {
|
|
82
|
+
errors: [
|
|
83
|
+
{
|
|
84
|
+
text: `'${directory}' does not exist or is not a directory.`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// TODO: Consider hashing the contents rather than using a unique identifier every time?
|
|
91
|
+
identifiers.set(directory, nanoid());
|
|
92
|
+
if (!buildOutputDirectory) {
|
|
93
|
+
console.warn(
|
|
94
|
+
"You're attempting to import static assets as part of your Pages Functions, but have not specified a directory in which to put them. You must use 'wrangler pages dev <directory>' rather than 'wrangler pages dev -- <command>' to import static assets in Functions."
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return { path: directory, namespace: "assets" };
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
pluginBuild.onLoad(
|
|
101
|
+
{ filter: /.*/, namespace: "assets" },
|
|
102
|
+
async (args) => {
|
|
103
|
+
const identifier = identifiers.get(args.path);
|
|
104
|
+
|
|
105
|
+
if (buildOutputDirectory) {
|
|
106
|
+
const staticAssetsOutputDirectory = join(
|
|
107
|
+
buildOutputDirectory,
|
|
108
|
+
"cdn-cgi",
|
|
109
|
+
"pages-plugins",
|
|
110
|
+
identifier as string
|
|
111
|
+
);
|
|
112
|
+
await rm(staticAssetsOutputDirectory, {
|
|
113
|
+
force: true,
|
|
114
|
+
recursive: true,
|
|
115
|
+
});
|
|
116
|
+
await cp(args.path, staticAssetsOutputDirectory, {
|
|
117
|
+
force: true,
|
|
118
|
+
recursive: true,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
// TODO: Watch args.path for changes and re-copy when updated
|
|
123
|
+
contents: `export const onRequest = ({ request, env, functionPath }) => {
|
|
124
|
+
const url = new URL(request.url)
|
|
125
|
+
const relativePathname = url.pathname.split(functionPath)[1] || "/";
|
|
126
|
+
url.pathname = '/cdn-cgi/pages-plugins/${identifier}' + relativePathname
|
|
127
|
+
request = new Request(url.toString(), request)
|
|
128
|
+
return env.ASSETS.fetch(request)
|
|
129
|
+
}`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
},
|
|
60
136
|
],
|
|
61
137
|
});
|
|
62
138
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFileSync } from "fs";
|
|
1
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
2
2
|
import { runInTempDir } from "../../src/__tests__/helpers/run-in-tmp";
|
|
3
3
|
import { toUrlPath } from "../../src/paths";
|
|
4
4
|
import { compareRoutes, generateConfigFromFileTree } from "./filepath-routing";
|
|
@@ -83,34 +83,144 @@ describe("filepath-routing", () => {
|
|
|
83
83
|
`
|
|
84
84
|
);
|
|
85
85
|
|
|
86
|
+
mkdirSync("todos");
|
|
87
|
+
|
|
88
|
+
writeFileSync(
|
|
89
|
+
"todos/[id].ts",
|
|
90
|
+
`
|
|
91
|
+
export function onRequestPost() {}
|
|
92
|
+
export function onRequestDelete() {}
|
|
93
|
+
`
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
mkdirSync("authors");
|
|
97
|
+
mkdirSync("authors/[authorId]");
|
|
98
|
+
mkdirSync("authors/[authorId]/todos");
|
|
99
|
+
|
|
100
|
+
writeFileSync(
|
|
101
|
+
"authors/[authorId]/todos/[todoId].ts",
|
|
102
|
+
`
|
|
103
|
+
export function onRequestPost() {}
|
|
104
|
+
`
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
mkdirSync("books");
|
|
108
|
+
|
|
109
|
+
writeFileSync(
|
|
110
|
+
"books/[[title]].ts",
|
|
111
|
+
`
|
|
112
|
+
export function onRequestPost() {}
|
|
113
|
+
`
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
mkdirSync("cats");
|
|
117
|
+
mkdirSync("cats/[[breed]]");
|
|
118
|
+
|
|
119
|
+
writeFileSync(
|
|
120
|
+
"cats/[[breed]]/blah.ts",
|
|
121
|
+
`
|
|
122
|
+
export function onRequestPost() {}
|
|
123
|
+
`
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// This won't actually work at runtime.
|
|
127
|
+
writeFileSync(
|
|
128
|
+
"cats/[[breed]]/[[name]].ts",
|
|
129
|
+
`
|
|
130
|
+
export function onRequestPost() {}
|
|
131
|
+
`
|
|
132
|
+
);
|
|
133
|
+
|
|
86
134
|
const entries = await generateConfigFromFileTree({
|
|
87
135
|
baseDir: ".",
|
|
88
136
|
baseURL: "/base" as UrlPath,
|
|
89
137
|
});
|
|
90
|
-
expect(entries).
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
138
|
+
expect(entries).toMatchInlineSnapshot(`
|
|
139
|
+
Object {
|
|
140
|
+
"routes": Array [
|
|
141
|
+
Object {
|
|
142
|
+
"method": "POST",
|
|
143
|
+
"module": Array [
|
|
144
|
+
"authors/[authorId]/todos/[todoId].ts:onRequestPost",
|
|
145
|
+
],
|
|
146
|
+
"mountPath": "/base/authors/:authorId/todos",
|
|
147
|
+
"routePath": "/base/authors/:authorId/todos/:todoId",
|
|
148
|
+
},
|
|
149
|
+
Object {
|
|
150
|
+
"method": "POST",
|
|
151
|
+
"module": Array [
|
|
152
|
+
"cats/[[breed]]/blah.ts:onRequestPost",
|
|
153
|
+
],
|
|
154
|
+
"mountPath": "/base/cats/:breed*",
|
|
155
|
+
"routePath": "/base/cats/:breed*/blah",
|
|
156
|
+
},
|
|
157
|
+
Object {
|
|
158
|
+
"method": "POST",
|
|
159
|
+
"module": Array [
|
|
160
|
+
"cats/[[breed]]/[[name]].ts:onRequestPost",
|
|
161
|
+
],
|
|
162
|
+
"mountPath": "/base/cats/:breed*",
|
|
163
|
+
"routePath": "/base/cats/:breed*/:name*",
|
|
164
|
+
},
|
|
165
|
+
Object {
|
|
166
|
+
"method": "DELETE",
|
|
167
|
+
"module": Array [
|
|
168
|
+
"todos/[id].ts:onRequestDelete",
|
|
169
|
+
],
|
|
170
|
+
"mountPath": "/base/todos",
|
|
171
|
+
"routePath": "/base/todos/:id",
|
|
172
|
+
},
|
|
173
|
+
Object {
|
|
174
|
+
"method": "POST",
|
|
175
|
+
"module": Array [
|
|
176
|
+
"todos/[id].ts:onRequestPost",
|
|
177
|
+
],
|
|
178
|
+
"mountPath": "/base/todos",
|
|
179
|
+
"routePath": "/base/todos/:id",
|
|
180
|
+
},
|
|
181
|
+
Object {
|
|
182
|
+
"method": "POST",
|
|
183
|
+
"module": Array [
|
|
184
|
+
"books/[[title]].ts:onRequestPost",
|
|
185
|
+
],
|
|
186
|
+
"mountPath": "/base/books",
|
|
187
|
+
"routePath": "/base/books/:title*",
|
|
188
|
+
},
|
|
189
|
+
Object {
|
|
190
|
+
"method": "DELETE",
|
|
191
|
+
"module": Array [
|
|
192
|
+
"bar.ts:onRequestDelete",
|
|
193
|
+
],
|
|
194
|
+
"mountPath": "/base/",
|
|
195
|
+
"routePath": "/base/bar",
|
|
196
|
+
},
|
|
197
|
+
Object {
|
|
198
|
+
"method": "PUT",
|
|
199
|
+
"module": Array [
|
|
200
|
+
"bar.ts:onRequestPut",
|
|
201
|
+
],
|
|
202
|
+
"mountPath": "/base/",
|
|
203
|
+
"routePath": "/base/bar",
|
|
204
|
+
},
|
|
205
|
+
Object {
|
|
206
|
+
"method": "GET",
|
|
207
|
+
"module": Array [
|
|
208
|
+
"foo.ts:onRequestGet",
|
|
209
|
+
],
|
|
210
|
+
"mountPath": "/base/",
|
|
211
|
+
"routePath": "/base/foo",
|
|
212
|
+
},
|
|
213
|
+
Object {
|
|
214
|
+
"method": "POST",
|
|
215
|
+
"module": Array [
|
|
216
|
+
"foo.ts:onRequestPost",
|
|
217
|
+
],
|
|
218
|
+
"mountPath": "/base/",
|
|
219
|
+
"routePath": "/base/foo",
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
}
|
|
223
|
+
`);
|
|
114
224
|
});
|
|
115
225
|
});
|
|
116
226
|
});
|
|
@@ -118,6 +228,7 @@ describe("filepath-routing", () => {
|
|
|
118
228
|
function routeConfig(routePath: string, method?: string): RouteConfig {
|
|
119
229
|
return {
|
|
120
230
|
routePath: toUrlPath(routePath),
|
|
231
|
+
mountPath: toUrlPath("/"),
|
|
121
232
|
method: method as HTTPMethod,
|
|
122
233
|
};
|
|
123
234
|
}
|
|
@@ -53,6 +53,7 @@ export async function generateConfigFromFileTree({
|
|
|
53
53
|
let routePath = path
|
|
54
54
|
.relative(baseDir, filepath)
|
|
55
55
|
.slice(0, -ext.length);
|
|
56
|
+
let mountPath = path.dirname(routePath);
|
|
56
57
|
|
|
57
58
|
if (isIndexFile || isMiddlewareFile) {
|
|
58
59
|
routePath = path.dirname(routePath);
|
|
@@ -61,17 +62,27 @@ export async function generateConfigFromFileTree({
|
|
|
61
62
|
if (routePath === ".") {
|
|
62
63
|
routePath = "";
|
|
63
64
|
}
|
|
65
|
+
if (mountPath === ".") {
|
|
66
|
+
mountPath = "";
|
|
67
|
+
}
|
|
64
68
|
|
|
65
69
|
routePath = `${baseURL}/${routePath}`;
|
|
70
|
+
mountPath = `${baseURL}/${mountPath}`;
|
|
71
|
+
|
|
72
|
+
routePath = routePath.replace(/\[\[([^\]]+)\]\]/g, ":$1*"); // transform [[id]] => :id*
|
|
73
|
+
routePath = routePath.replaceAll(/\[([^\]]+)\]/g, ":$1"); // transform [id] => :id
|
|
74
|
+
mountPath = mountPath.replace(/\[\[([^\]]+)\]\]/g, ":$1*"); // transform [[id]] => :id*
|
|
75
|
+
mountPath = mountPath.replaceAll(/\[([^\]]+)\]/g, ":$1"); // transform [id] => :id
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
// These are used as module specifiers so UrlPaths are okay to use even on Windows
|
|
78
|
+
const modulePath = toUrlPath(path.relative(baseDir, filepath));
|
|
69
79
|
|
|
70
80
|
const routeEntry: RouteConfig = {
|
|
71
81
|
routePath: toUrlPath(routePath),
|
|
82
|
+
mountPath: toUrlPath(mountPath),
|
|
72
83
|
method: method.toUpperCase() as HTTPMethod,
|
|
73
84
|
[isMiddlewareFile ? "middleware" : "module"]: [
|
|
74
|
-
`${
|
|
85
|
+
`${modulePath}:${exportName}`,
|
|
75
86
|
],
|
|
76
87
|
};
|
|
77
88
|
|
|
@@ -21,6 +21,7 @@ export function isHTTPMethod(
|
|
|
21
21
|
|
|
22
22
|
export type RoutesCollection = Array<{
|
|
23
23
|
routePath: UrlPath;
|
|
24
|
+
mountPath: UrlPath;
|
|
24
25
|
method?: HTTPMethod;
|
|
25
26
|
modules: string[];
|
|
26
27
|
middlewares: string[];
|
|
@@ -33,6 +34,7 @@ export type Config = {
|
|
|
33
34
|
|
|
34
35
|
export type RouteConfig = {
|
|
35
36
|
routePath: UrlPath;
|
|
37
|
+
mountPath: UrlPath;
|
|
36
38
|
method?: HTTPMethod;
|
|
37
39
|
middleware?: string | string[];
|
|
38
40
|
module?: string | string[];
|
|
@@ -113,9 +115,11 @@ export function parseConfig(config: Config, baseDir: string) {
|
|
|
113
115
|
});
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
for (const { routePath, method, ...props } of config.routes ??
|
|
118
|
+
for (const { routePath, mountPath, method, ...props } of config.routes ??
|
|
119
|
+
[]) {
|
|
117
120
|
routes.push({
|
|
118
121
|
routePath,
|
|
122
|
+
mountPath,
|
|
119
123
|
method,
|
|
120
124
|
middlewares: parseModuleIdentifiers(props.middleware),
|
|
121
125
|
modules: parseModuleIdentifiers(props.module),
|
|
@@ -141,6 +145,7 @@ export const routes = [
|
|
|
141
145
|
.map(
|
|
142
146
|
(route) => ` {
|
|
143
147
|
routePath: "${route.routePath}",
|
|
148
|
+
mountPath: "${route.mountPath}",
|
|
144
149
|
method: "${route.method}",
|
|
145
150
|
middlewares: [${route.middlewares.join(", ")}],
|
|
146
151
|
modules: [${route.modules.join(", ")}],
|