wrangler 0.0.0-e64812e → 0.0.0-e8e684c7

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 (214) hide show
  1. package/README.md +27 -3
  2. package/bin/wrangler.js +150 -62
  3. package/kv-asset-handler.js +1 -0
  4. package/miniflare-dist/index.mjs +663 -7
  5. package/package.json +173 -152
  6. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  7. package/src/__tests__/config-cache.test.ts +30 -24
  8. package/src/__tests__/configuration.test.ts +4015 -3363
  9. package/src/__tests__/dev.test.tsx +1290 -952
  10. package/src/__tests__/generate.test.ts +91 -0
  11. package/src/__tests__/guess-worker-format.test.ts +68 -68
  12. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  13. package/src/__tests__/helpers/mock-account-id.ts +24 -24
  14. package/src/__tests__/helpers/mock-bin.ts +20 -20
  15. package/src/__tests__/helpers/mock-cfetch.ts +179 -94
  16. package/src/__tests__/helpers/mock-console.ts +49 -39
  17. package/src/__tests__/helpers/mock-dialogs.ts +94 -71
  18. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  19. package/src/__tests__/helpers/mock-http-server.ts +30 -30
  20. package/src/__tests__/helpers/mock-istty.ts +65 -18
  21. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  22. package/src/__tests__/helpers/mock-kv.ts +26 -26
  23. package/src/__tests__/helpers/mock-oauth-flow.ts +223 -228
  24. package/src/__tests__/helpers/mock-process.ts +39 -0
  25. package/src/__tests__/helpers/mock-stdin.ts +82 -77
  26. package/src/__tests__/helpers/mock-web-socket.ts +21 -21
  27. package/src/__tests__/helpers/run-in-tmp.ts +27 -28
  28. package/src/__tests__/helpers/run-wrangler.ts +8 -8
  29. package/src/__tests__/helpers/write-worker-source.ts +16 -16
  30. package/src/__tests__/helpers/write-wrangler-toml.ts +9 -9
  31. package/src/__tests__/https-options.test.ts +142 -109
  32. package/src/__tests__/index.test.ts +222 -233
  33. package/src/__tests__/init.test.ts +2139 -1603
  34. package/src/__tests__/jest.setup.ts +114 -34
  35. package/src/__tests__/kv.test.ts +1222 -1032
  36. package/src/__tests__/logger.test.ts +100 -74
  37. package/src/__tests__/metrics.test.ts +450 -0
  38. package/src/__tests__/package-manager.test.ts +303 -303
  39. package/src/__tests__/pages.test.ts +1173 -652
  40. package/src/__tests__/parse.test.ts +252 -252
  41. package/src/__tests__/publish.test.ts +6788 -5301
  42. package/src/__tests__/pubsub.test.ts +367 -0
  43. package/src/__tests__/r2.test.ts +328 -225
  44. package/src/__tests__/route.test.ts +18 -18
  45. package/src/__tests__/secret.test.ts +470 -459
  46. package/src/__tests__/tail.test.ts +676 -564
  47. package/src/__tests__/test-old-node-version.js +31 -0
  48. package/src/__tests__/user.test.ts +136 -112
  49. package/src/__tests__/validate-dev-props.test.ts +56 -0
  50. package/src/__tests__/version.test.ts +35 -0
  51. package/src/__tests__/whoami.test.tsx +267 -83
  52. package/src/__tests__/worker-namespace.test.ts +329 -0
  53. package/src/abort.d.ts +1 -1
  54. package/src/api/dev.ts +93 -0
  55. package/src/api/index.ts +1 -0
  56. package/src/bundle-reporter.tsx +29 -0
  57. package/src/bundle.ts +430 -157
  58. package/src/cfetch/index.ts +80 -80
  59. package/src/cfetch/internal.ts +244 -91
  60. package/src/cli.ts +21 -7
  61. package/src/config/config.ts +224 -195
  62. package/src/config/diagnostics.ts +61 -61
  63. package/src/config/environment.ts +422 -357
  64. package/src/config/index.ts +219 -193
  65. package/src/config/validation-helpers.ts +379 -357
  66. package/src/config/validation.ts +1690 -1367
  67. package/src/config-cache.ts +81 -41
  68. package/src/create-worker-preview.ts +208 -136
  69. package/src/create-worker-upload-form.ts +275 -238
  70. package/src/dev/dev-vars.ts +13 -13
  71. package/src/dev/dev.tsx +477 -351
  72. package/src/dev/local.tsx +426 -295
  73. package/src/dev/remote.tsx +406 -211
  74. package/src/dev/use-esbuild.ts +154 -94
  75. package/src/dev/validate-dev-props.ts +31 -0
  76. package/src/dev-registry.tsx +160 -0
  77. package/src/dev.tsx +696 -0
  78. package/src/dialogs.tsx +97 -97
  79. package/src/dispatch-namespace.ts +206 -0
  80. package/src/durable.ts +87 -87
  81. package/src/entry.ts +234 -228
  82. package/src/environment-variables.ts +23 -23
  83. package/src/errors.ts +6 -6
  84. package/src/faye-websocket.d.ts +6 -0
  85. package/src/generate.ts +131 -0
  86. package/src/git-client.ts +42 -0
  87. package/src/global-wrangler-config-path.ts +26 -0
  88. package/src/https-options.ts +84 -80
  89. package/src/index.tsx +2164 -2768
  90. package/src/init.ts +622 -0
  91. package/src/inspect.ts +678 -593
  92. package/src/intl-polyfill.d.ts +123 -123
  93. package/src/is-ci.ts +14 -0
  94. package/src/is-interactive.ts +12 -0
  95. package/src/kv.ts +299 -277
  96. package/src/logger.ts +46 -39
  97. package/src/metrics/index.ts +5 -0
  98. package/src/metrics/metrics-config.ts +239 -0
  99. package/src/metrics/metrics-dispatcher.ts +96 -0
  100. package/src/metrics/metrics-usage-headers.ts +24 -0
  101. package/src/metrics/send-event.ts +92 -0
  102. package/src/miniflare-cli/assets.ts +554 -0
  103. package/src/miniflare-cli/enum-keys.ts +8 -8
  104. package/src/miniflare-cli/index.ts +198 -32
  105. package/src/miniflare-cli/request-context.ts +18 -18
  106. package/src/module-collection.ts +212 -212
  107. package/src/open-in-browser.ts +4 -6
  108. package/src/package-manager.ts +173 -128
  109. package/src/pages/build.tsx +238 -0
  110. package/src/pages/constants.ts +12 -0
  111. package/src/pages/deployments.tsx +106 -0
  112. package/src/pages/dev.tsx +528 -0
  113. package/src/pages/errors.ts +22 -0
  114. package/src/pages/functions/buildPlugin.ts +109 -0
  115. package/src/pages/functions/buildWorker.ts +155 -0
  116. package/{pages → src/pages}/functions/filepath-routing.test.ts +113 -113
  117. package/src/pages/functions/filepath-routing.ts +189 -0
  118. package/src/pages/functions/identifiers.ts +78 -0
  119. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  120. package/src/pages/functions/routes-consolidation.ts +73 -0
  121. package/src/pages/functions/routes-transformation.test.ts +271 -0
  122. package/src/pages/functions/routes-transformation.ts +122 -0
  123. package/src/pages/functions/routes.ts +151 -0
  124. package/src/pages/functions.tsx +96 -0
  125. package/src/pages/index.tsx +94 -0
  126. package/src/pages/projects.tsx +169 -0
  127. package/src/pages/publish.tsx +391 -0
  128. package/src/pages/types.ts +49 -0
  129. package/src/pages/upload.tsx +413 -0
  130. package/src/pages/utils.ts +12 -0
  131. package/src/parse.ts +209 -125
  132. package/src/paths.ts +6 -6
  133. package/src/preview.ts +31 -0
  134. package/src/proxy.ts +410 -389
  135. package/src/publish.ts +773 -534
  136. package/src/pubsub/index.ts +286 -0
  137. package/src/pubsub/pubsub-commands.tsx +623 -0
  138. package/src/r2.ts +100 -19
  139. package/src/selfsigned.d.ts +23 -23
  140. package/src/sites.tsx +278 -201
  141. package/src/tail/filters.ts +111 -109
  142. package/src/tail/index.ts +230 -217
  143. package/src/tail/printing.ts +86 -46
  144. package/src/update-check.ts +11 -11
  145. package/src/user/choose-account.tsx +69 -0
  146. package/src/user/env-vars.ts +46 -0
  147. package/src/user/generate-auth-url.ts +33 -0
  148. package/src/user/generate-random-state.ts +16 -0
  149. package/src/user/index.ts +3 -0
  150. package/src/user/user.tsx +1191 -0
  151. package/src/whoami.tsx +141 -42
  152. package/src/worker.ts +123 -99
  153. package/src/zones.ts +167 -0
  154. package/templates/checked-fetch.js +17 -0
  155. package/templates/first-party-worker-module-facade.ts +18 -0
  156. package/templates/format-dev-errors.ts +32 -0
  157. package/templates/new-worker-scheduled.js +3 -3
  158. package/templates/new-worker-scheduled.ts +15 -15
  159. package/templates/new-worker.js +3 -3
  160. package/templates/new-worker.ts +15 -15
  161. package/templates/no-op-worker.js +10 -0
  162. package/templates/pages-shim.ts +9 -0
  163. package/templates/pages-template-plugin.ts +167 -0
  164. package/templates/pages-template-worker.ts +172 -0
  165. package/templates/serve-static-assets.ts +63 -0
  166. package/templates/service-bindings-module-facade.js +54 -0
  167. package/templates/service-bindings-sw-facade.js +42 -0
  168. package/templates/tsconfig.json +95 -95
  169. package/wrangler-dist/cli.d.ts +338 -0
  170. package/wrangler-dist/cli.js +95449 -67524
  171. package/pages/functions/buildPlugin.ts +0 -105
  172. package/pages/functions/buildWorker.ts +0 -151
  173. package/pages/functions/filepath-routing.ts +0 -189
  174. package/pages/functions/identifiers.ts +0 -78
  175. package/pages/functions/routes.ts +0 -156
  176. package/pages/functions/template-plugin.ts +0 -147
  177. package/pages/functions/template-worker.ts +0 -143
  178. package/src/__tests__/helpers/faye-websocket.d.ts +0 -6
  179. package/src/pages.tsx +0 -2038
  180. package/src/user.tsx +0 -1203
  181. package/templates/static-asset-facade.js +0 -47
  182. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  183. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  184. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  185. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  186. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  187. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  188. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  189. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  190. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  191. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  192. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  193. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  194. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  195. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  196. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  197. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  198. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  199. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  200. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  201. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  202. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  203. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  204. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  205. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  206. package/vendor/wrangler-mime/LICENSE +0 -21
  207. package/vendor/wrangler-mime/Mime.js +0 -97
  208. package/vendor/wrangler-mime/README.md +0 -187
  209. package/vendor/wrangler-mime/cli.js +0 -46
  210. package/vendor/wrangler-mime/index.js +0 -4
  211. package/vendor/wrangler-mime/lite.js +0 -4
  212. package/vendor/wrangler-mime/package.json +0 -52
  213. package/vendor/wrangler-mime/types/other.js +0 -1
  214. package/vendor/wrangler-mime/types/standard.js +0 -1
@@ -0,0 +1,106 @@
1
+ import { render, Text } from "ink";
2
+ import SelectInput from "ink-select-input";
3
+ import Table from "ink-table";
4
+ import React from "react";
5
+ import { format as timeagoFormat } from "timeago.js";
6
+ import { fetchResult } from "../cfetch";
7
+ import { getConfigCache, saveToConfigCache } from "../config-cache";
8
+ import { FatalError } from "../errors";
9
+ import * as metrics from "../metrics";
10
+ import { requireAuth } from "../user";
11
+ import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
12
+ import { listProjects } from "./projects";
13
+ import { pagesBetaWarning } from "./utils";
14
+ import type {
15
+ Deployment,
16
+ PagesConfigCache,
17
+ YargsOptionsToInterface,
18
+ } from "./types";
19
+ import type { Argv } from "yargs";
20
+
21
+ type ListArgs = YargsOptionsToInterface<typeof ListOptions>;
22
+
23
+ export function ListOptions(yargs: Argv) {
24
+ return yargs
25
+ .options({
26
+ "project-name": {
27
+ type: "string",
28
+ description:
29
+ "The name of the project you would like to list deployments for",
30
+ },
31
+ })
32
+ .epilogue(pagesBetaWarning);
33
+ }
34
+
35
+ export async function ListHandler({ projectName }: ListArgs) {
36
+ const config = getConfigCache<PagesConfigCache>(PAGES_CONFIG_CACHE_FILENAME);
37
+ const accountId = await requireAuth(config);
38
+
39
+ projectName ??= config.project_name;
40
+
41
+ const isInteractive = process.stdin.isTTY;
42
+ if (!projectName && isInteractive) {
43
+ const projects = await listProjects({ accountId });
44
+ projectName = await new Promise((resolve) => {
45
+ const { unmount } = render(
46
+ <>
47
+ <Text>Select a project:</Text>
48
+ <SelectInput
49
+ items={projects.map((project) => ({
50
+ key: project.name,
51
+ label: project.name,
52
+ value: project,
53
+ }))}
54
+ onSelect={async (selected) => {
55
+ resolve(selected.value.name);
56
+ unmount();
57
+ }}
58
+ />
59
+ </>
60
+ );
61
+ });
62
+ }
63
+
64
+ if (!projectName) {
65
+ throw new FatalError("Must specify a project name.", 1);
66
+ }
67
+
68
+ const deployments: Array<Deployment> = await fetchResult(
69
+ `/accounts/${accountId}/pages/projects/${projectName}/deployments`
70
+ );
71
+
72
+ const titleCase = (word: string) =>
73
+ word.charAt(0).toUpperCase() + word.slice(1);
74
+
75
+ const shortSha = (sha: string) => sha.slice(0, 7);
76
+
77
+ const getStatus = (deployment: Deployment) => {
78
+ // Return a pretty time since timestamp if successful otherwise the status
79
+ if (deployment.latest_stage.status === `success`) {
80
+ return timeagoFormat(deployment.latest_stage.ended_on);
81
+ }
82
+ return titleCase(deployment.latest_stage.status);
83
+ };
84
+
85
+ const data = deployments.map((deployment) => {
86
+ return {
87
+ Environment: titleCase(deployment.environment),
88
+ Branch: deployment.deployment_trigger.metadata.branch,
89
+ Source: shortSha(deployment.deployment_trigger.metadata.commit_hash),
90
+ Deployment: deployment.url,
91
+ Status: getStatus(deployment),
92
+ // TODO: Use a url shortener
93
+ Build: `https://dash.cloudflare.com/${accountId}/pages/view/${deployment.project_name}/${deployment.id}`,
94
+ };
95
+ });
96
+
97
+ saveToConfigCache<PagesConfigCache>(PAGES_CONFIG_CACHE_FILENAME, {
98
+ account_id: accountId,
99
+ });
100
+
101
+ const { unmount } = render(<Table data={data}></Table>, {
102
+ patchConsole: false,
103
+ });
104
+ unmount();
105
+ await metrics.sendMetricsEvent("list pages deployments");
106
+ }
@@ -0,0 +1,528 @@
1
+ import { execSync, spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { homedir, tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { watch } from "chokidar";
6
+ import { build as workerJsBuild } from "esbuild";
7
+ import { unstable_dev } from "../api";
8
+ import { FatalError } from "../errors";
9
+ import { logger } from "../logger";
10
+ import * as metrics from "../metrics";
11
+ import { buildFunctions } from "./build";
12
+ import { SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
13
+ import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
14
+ import { CLEANUP, CLEANUP_CALLBACKS, pagesBetaWarning } from "./utils";
15
+ import type { AdditionalDevProps } from "../dev";
16
+ import type { YargsOptionsToInterface } from "./types";
17
+ import type { Plugin } from "esbuild";
18
+ import type { Argv } from "yargs";
19
+
20
+ const DURABLE_OBJECTS_BINDING_REGEXP = new RegExp(
21
+ /^(?<binding>[^=]+)=(?<className>[^@\s]+)(@(?<scriptName>.*)$)?$/
22
+ );
23
+
24
+ type PagesDevArgs = YargsOptionsToInterface<typeof Options>;
25
+
26
+ export function Options(yargs: Argv) {
27
+ return yargs
28
+ .positional("directory", {
29
+ type: "string",
30
+ demandOption: undefined,
31
+ description: "The directory of static assets to serve",
32
+ })
33
+ .positional("command", {
34
+ type: "string",
35
+ demandOption: undefined,
36
+ description: "The proxy command to run",
37
+ })
38
+ .options({
39
+ local: {
40
+ type: "boolean",
41
+ default: true,
42
+ description: "Run on my machine",
43
+ },
44
+ "compatibility-date": {
45
+ describe: "Date to use for compatibility checks",
46
+ type: "string",
47
+ },
48
+ "compatibility-flags": {
49
+ describe: "Flags to use for compatibility checks",
50
+ alias: "compatibility-flag",
51
+ type: "string",
52
+ array: true,
53
+ },
54
+
55
+ // TODO
56
+ // For now, all Pages projects are set to 2021-11-02. We're adding compat date soon, and we can then adopt `wrangler dev`'s `default: true`.
57
+ // However, it looks like it isn't actually connected up properly in `wrangler dev` at the moment, hence commenting this out for now.
58
+
59
+ // latest: {
60
+ // describe: "Use the latest version of the worker runtime",
61
+ // type: "boolean",
62
+ // default: false,
63
+ // },
64
+
65
+ ip: {
66
+ type: "string",
67
+ default: "0.0.0.0",
68
+ description: "The IP address to listen on",
69
+ },
70
+ port: {
71
+ type: "number",
72
+ default: 8788,
73
+ description: "The port to listen on (serve from)",
74
+ },
75
+ "inspector-port": {
76
+ type: "number",
77
+ describe: "Port for devtools to connect to",
78
+ },
79
+ proxy: {
80
+ type: "number",
81
+ description: "The port to proxy (where the static assets are served)",
82
+ },
83
+ "script-path": {
84
+ type: "string",
85
+ default: "_worker.js",
86
+ description:
87
+ "The location of the single Worker script if not using functions",
88
+ },
89
+ binding: {
90
+ type: "array",
91
+ description: "Bind variable/secret (KEY=VALUE)",
92
+ alias: "b",
93
+ },
94
+ kv: {
95
+ type: "array",
96
+ description: "KV namespace to bind (--kv KV_BINDING)",
97
+ alias: "k",
98
+ },
99
+ do: {
100
+ type: "array",
101
+ description: "Durable Object to bind (--do NAME=CLASS)",
102
+ alias: "o",
103
+ },
104
+ r2: {
105
+ type: "array",
106
+ description: "R2 bucket to bind (--r2 R2_BINDING)",
107
+ },
108
+ "live-reload": {
109
+ type: "boolean",
110
+ default: false,
111
+ description: "Auto reload HTML pages when change is detected",
112
+ },
113
+ "local-protocol": {
114
+ describe: "Protocol to listen to requests on, defaults to http.",
115
+ choices: ["http", "https"] as const,
116
+ },
117
+ "experimental-enable-local-persistence": {
118
+ type: "boolean",
119
+ default: false,
120
+ describe: "Enable persistence for this session (only for local mode)",
121
+ },
122
+ "node-compat": {
123
+ describe: "Enable node.js compatibility",
124
+ default: false,
125
+ type: "boolean",
126
+ hidden: true,
127
+ },
128
+ config: {
129
+ describe: "Pages does not support wrangler.toml",
130
+ type: "string",
131
+ hidden: true,
132
+ },
133
+ })
134
+ .epilogue(pagesBetaWarning);
135
+ }
136
+
137
+ export const Handler = async ({
138
+ local,
139
+ directory,
140
+ "compatibility-date": compatibilityDate = "2021-11-02",
141
+ "compatibility-flags": compatibilityFlags,
142
+ ip,
143
+ port,
144
+ "inspector-port": inspectorPort,
145
+ proxy: requestedProxyPort,
146
+ "script-path": singleWorkerScriptPath,
147
+ binding: bindings = [],
148
+ kv: kvs = [],
149
+ do: durableObjects = [],
150
+ r2: r2s = [],
151
+ "live-reload": liveReload,
152
+ "local-protocol": localProtocol,
153
+ "experimental-enable-local-persistence": experimentalEnableLocalPersistence,
154
+ "node-compat": nodeCompat,
155
+ config: config,
156
+ _: [_pages, _dev, ...remaining],
157
+ }: PagesDevArgs) => {
158
+ // Beta message for `wrangler pages <commands>` usage
159
+ logger.log(pagesBetaWarning);
160
+
161
+ if (!local) {
162
+ throw new FatalError("Only local mode is supported at the moment.", 1);
163
+ }
164
+
165
+ if (config) {
166
+ throw new FatalError("Pages does not support wrangler.toml", 1);
167
+ }
168
+
169
+ const functionsDirectory = "./functions";
170
+ let usingFunctions = existsSync(functionsDirectory);
171
+
172
+ const command = remaining;
173
+
174
+ let proxyPort: number | undefined;
175
+
176
+ if (directory !== undefined && command.length > 0) {
177
+ throw new FatalError(
178
+ "Specify either a directory OR a proxy command, not both.",
179
+ 1
180
+ );
181
+ } else if (directory === undefined) {
182
+ proxyPort = await spawnProxyProcess({
183
+ port: requestedProxyPort,
184
+ command,
185
+ });
186
+ if (proxyPort === undefined) return undefined;
187
+ } else {
188
+ directory = resolve(directory);
189
+ }
190
+
191
+ let scriptReadyResolve: () => void;
192
+ const scriptReadyPromise = new Promise<void>(
193
+ (promiseResolve) => (scriptReadyResolve = promiseResolve)
194
+ );
195
+
196
+ let scriptPath = "";
197
+
198
+ // Try to use Functions
199
+ if (usingFunctions) {
200
+ const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
201
+ scriptPath = outfile;
202
+
203
+ if (nodeCompat) {
204
+ console.warn(
205
+ "Enabling node.js compatibility mode for builtins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
206
+ );
207
+ }
208
+
209
+ logger.log(`Compiling worker to "${outfile}"...`);
210
+ const onEnd = () => scriptReadyResolve();
211
+ try {
212
+ await buildFunctions({
213
+ outfile,
214
+ functionsDirectory,
215
+ sourcemap: true,
216
+ watch: true,
217
+ onEnd,
218
+ buildOutputDirectory: directory,
219
+ nodeCompat,
220
+ });
221
+ await metrics.sendMetricsEvent("build pages functions");
222
+
223
+ // If Functions found routes, continue using Functions
224
+ watch([functionsDirectory], {
225
+ persistent: true,
226
+ ignoreInitial: true,
227
+ }).on("all", async () => {
228
+ try {
229
+ await buildFunctions({
230
+ outfile,
231
+ functionsDirectory,
232
+ sourcemap: true,
233
+ watch: true,
234
+ onEnd,
235
+ buildOutputDirectory: directory,
236
+ nodeCompat,
237
+ });
238
+ await metrics.sendMetricsEvent("build pages functions");
239
+ } catch (e) {
240
+ if (e instanceof FunctionsNoRoutesError) {
241
+ logger.warn(
242
+ getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
243
+ );
244
+ } else {
245
+ throw e;
246
+ }
247
+ }
248
+ });
249
+ } catch (e) {
250
+ // If there are no Functions, then Pages will only serve assets.
251
+ if (e instanceof FunctionsNoRoutesError) {
252
+ logger.warn(
253
+ getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
254
+ );
255
+ // Resolve anyway and run without Functions
256
+ onEnd();
257
+ // Turn off Functions
258
+ usingFunctions = false;
259
+ } else {
260
+ throw e;
261
+ }
262
+ }
263
+ }
264
+ // Depending on the result of building Functions, we may not actually be using
265
+ // Functions even if the directory exists.
266
+ if (!usingFunctions) {
267
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
268
+ scriptReadyResolve!();
269
+
270
+ scriptPath =
271
+ directory !== undefined
272
+ ? join(directory, singleWorkerScriptPath)
273
+ : singleWorkerScriptPath;
274
+
275
+ if (!existsSync(scriptPath)) {
276
+ logger.log("No functions. Shimming...");
277
+ scriptPath = resolve(__dirname, "../templates/pages-shim.ts");
278
+ } else {
279
+ const runBuild = async () => {
280
+ try {
281
+ await workerJsBuild({
282
+ entryPoints: [scriptPath],
283
+ write: false,
284
+ plugins: [blockWorkerJsImports],
285
+ });
286
+ } catch {}
287
+ };
288
+ await runBuild();
289
+ watch([scriptPath], {
290
+ persistent: true,
291
+ ignoreInitial: true,
292
+ }).on("all", async () => {
293
+ await runBuild();
294
+ });
295
+ }
296
+ }
297
+
298
+ await scriptReadyPromise;
299
+
300
+ if (scriptPath === "") {
301
+ // Failed to get a script with or without Functions,
302
+ // something really bad must have happend.
303
+ throw new FatalError(
304
+ "Failed to start wrangler pages dev due to an unknown error",
305
+ 1
306
+ );
307
+ }
308
+
309
+ const { stop, waitUntilExit } = await unstable_dev(
310
+ scriptPath,
311
+ {
312
+ ip,
313
+ port,
314
+ inspectorPort,
315
+ watch: true,
316
+ localProtocol,
317
+ liveReload,
318
+ compatibilityDate,
319
+ compatibilityFlags,
320
+ nodeCompat,
321
+ vars: Object.fromEntries(
322
+ bindings
323
+ .map((binding) => binding.toString().split("="))
324
+ .map(([key, ...values]) => [key, values.join("=")])
325
+ ),
326
+ kv: kvs.map((val) => ({
327
+ binding: val.toString(),
328
+ id: "",
329
+ })),
330
+ durableObjects: durableObjects
331
+ .map((durableObject) => {
332
+ const { binding, className, scriptName } =
333
+ DURABLE_OBJECTS_BINDING_REGEXP.exec(durableObject.toString())
334
+ ?.groups || {};
335
+
336
+ if (!binding || !className) {
337
+ logger.warn(
338
+ "Could not parse Durable Object binding:",
339
+ durableObject.toString()
340
+ );
341
+ return;
342
+ }
343
+
344
+ return {
345
+ name: binding,
346
+ class_name: className,
347
+ script_name: scriptName,
348
+ };
349
+ })
350
+ .filter(Boolean) as AdditionalDevProps["durableObjects"],
351
+ r2: r2s.map((binding) => {
352
+ return { binding: binding.toString(), bucket_name: "" };
353
+ }),
354
+
355
+ enablePagesAssetsServiceBinding: {
356
+ proxyPort,
357
+ directory,
358
+ },
359
+ forceLocal: true,
360
+ experimentalEnableLocalPersistence,
361
+ showInteractiveDevSession: undefined,
362
+ inspect: true,
363
+ logLevel: "error",
364
+ logPrefix: "pages",
365
+ },
366
+ true
367
+ );
368
+ await metrics.sendMetricsEvent("run pages dev");
369
+
370
+ CLEANUP_CALLBACKS.push(stop);
371
+
372
+ waitUntilExit().then(() => {
373
+ CLEANUP();
374
+ process.exit(0);
375
+ });
376
+
377
+ process.on("exit", CLEANUP);
378
+ process.on("SIGINT", CLEANUP);
379
+ process.on("SIGTERM", CLEANUP);
380
+ };
381
+
382
+ function isWindows() {
383
+ return process.platform === "win32";
384
+ }
385
+
386
+ async function sleep(ms: number) {
387
+ await new Promise((promiseResolve) => setTimeout(promiseResolve, ms));
388
+ }
389
+
390
+ function getPids(pid: number) {
391
+ const pids: number[] = [pid];
392
+ let command: string, regExp: RegExp;
393
+
394
+ if (isWindows()) {
395
+ command = `wmic process where (ParentProcessId=${pid}) get ProcessId`;
396
+ regExp = new RegExp(/(\d+)/);
397
+ } else {
398
+ command = `pgrep -P ${pid}`;
399
+ regExp = new RegExp(/(\d+)/);
400
+ }
401
+
402
+ try {
403
+ const newPids = (
404
+ execSync(command)
405
+ .toString()
406
+ .split("\n")
407
+ .map((line) => line.match(regExp))
408
+ .filter((line) => line !== null) as RegExpExecArray[]
409
+ ).map((match) => parseInt(match[1]));
410
+
411
+ pids.push(...newPids.map(getPids).flat());
412
+ } catch {}
413
+
414
+ return pids;
415
+ }
416
+
417
+ function getPort(pid: number) {
418
+ let command: string, regExp: RegExp;
419
+
420
+ if (isWindows()) {
421
+ const drive = homedir().split(":\\")[0];
422
+ command = drive + ":\\windows\\system32\\netstat.exe -nao";
423
+ regExp = new RegExp(`TCP\\s+.*:(\\d+)\\s+.*:\\d+\\s+LISTENING\\s+${pid}`);
424
+ } else {
425
+ command = "lsof -nPi";
426
+ regExp = new RegExp(`${pid}\\s+.*TCP\\s+.*:(\\d+)\\s+\\(LISTEN\\)`);
427
+ }
428
+
429
+ try {
430
+ const matches = execSync(command)
431
+ .toString()
432
+ .split("\n")
433
+ .map((line) => line.match(regExp))
434
+ .filter((line) => line !== null) as RegExpExecArray[];
435
+
436
+ const match = matches[0];
437
+ if (match) return parseInt(match[1]);
438
+ } catch (thrown) {
439
+ logger.error(
440
+ `Error scanning for ports of process with PID ${pid}: ${thrown}`
441
+ );
442
+ }
443
+ }
444
+
445
+ async function spawnProxyProcess({
446
+ port,
447
+ command,
448
+ }: {
449
+ port?: number;
450
+ command: (string | number)[];
451
+ }): Promise<undefined | number> {
452
+ if (command.length === 0) {
453
+ CLEANUP();
454
+ throw new FatalError(
455
+ "Must specify a directory of static assets to serve or a command to run.",
456
+ 1
457
+ );
458
+ }
459
+
460
+ logger.log(`Running ${command.join(" ")}...`);
461
+ const proxy = spawn(
462
+ command[0].toString(),
463
+ command.slice(1).map((value) => value.toString()),
464
+ {
465
+ shell: isWindows(),
466
+ env: {
467
+ BROWSER: "none",
468
+ ...process.env,
469
+ },
470
+ }
471
+ );
472
+ CLEANUP_CALLBACKS.push(() => {
473
+ proxy.kill();
474
+ });
475
+
476
+ proxy.stdout.on("data", (data) => {
477
+ logger.log(`[proxy]: ${data}`);
478
+ });
479
+
480
+ proxy.stderr.on("data", (data) => {
481
+ logger.error(`[proxy]: ${data}`);
482
+ });
483
+
484
+ proxy.on("close", (code) => {
485
+ logger.error(`Proxy exited with status ${code}.`);
486
+ CLEANUP();
487
+ process.exitCode = code ?? 0;
488
+ });
489
+
490
+ // Wait for proxy process to start...
491
+ while (!proxy.pid) {}
492
+
493
+ if (port === undefined) {
494
+ logger.log(
495
+ `Sleeping ${SECONDS_TO_WAIT_FOR_PROXY} seconds to allow proxy process to start before attempting to automatically determine port...`
496
+ );
497
+ logger.log("To skip, specify the proxy port with --proxy.");
498
+ await sleep(SECONDS_TO_WAIT_FOR_PROXY * 1000);
499
+
500
+ port = getPids(proxy.pid)
501
+ .map(getPort)
502
+ .filter((nr) => nr !== undefined)[0];
503
+
504
+ if (port === undefined) {
505
+ CLEANUP();
506
+ throw new FatalError(
507
+ "Could not automatically determine proxy port. Please specify the proxy port with --proxy.",
508
+ 1
509
+ );
510
+ } else {
511
+ logger.log(`Automatically determined the proxy port to be ${port}.`);
512
+ }
513
+ }
514
+
515
+ return port;
516
+ }
517
+
518
+ const blockWorkerJsImports: Plugin = {
519
+ name: "block-worker-js-imports",
520
+ setup(build) {
521
+ build.onResolve({ filter: /.*/g }, (_args) => {
522
+ logger.error(
523
+ `_worker.js is importing from another file. This will throw an error if deployed.\nYou should bundle your Worker or remove the import if it is unused.`
524
+ );
525
+ return null;
526
+ });
527
+ },
528
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pages error when no routes are found in the functions directory
3
+ */
4
+ export class FunctionsNoRoutesError extends Error {
5
+ constructor(message: string) {
6
+ super(message);
7
+ }
8
+ }
9
+ /**
10
+ * Exit code for `pages functions build` when no routes are found.
11
+ */
12
+ export const EXIT_CODE_FUNCTIONS_NO_ROUTES_ERROR = 156;
13
+
14
+ /** Warning message for when buildFunctions throws FunctionsNoRoutesError */
15
+ export function getFunctionsNoRoutesWarning(
16
+ functionsDirectory: string,
17
+ suffix?: string
18
+ ) {
19
+ return `No routes found when building Functions directory: ${functionsDirectory}${
20
+ suffix ? " - " + suffix : ""
21
+ }`;
22
+ }