@jundle/cloudflare 0.0.0-stage → 0.0.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/package.json +22 -3
- package/src/bindings.js +295 -0
- package/src/build.d.ts +145 -0
- package/src/build.js +327 -0
- package/src/config.js +82 -0
- package/src/harness.js +707 -0
- package/src/index.d.ts +389 -0
- package/src/index.js +446 -0
- package/README.md +0 -3
package/src/build.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// @jundle/cloudflare — the Cloudflare Workers BUILD plugin
|
|
2
|
+
// (SPEC §16.13). The runtime plugin (src/index.js) is dev-only;
|
|
3
|
+
// this is its build half, auto-registered by jundle core when a
|
|
4
|
+
// build entry's runtime is "@jundle/cloudflare" (runtime
|
|
5
|
+
// implies it, SPEC §16.13): core appends it to config.plugins
|
|
6
|
+
// before the inventory is built, with options { configDir,
|
|
7
|
+
// wranglerConfig }, and merges its externals into every
|
|
8
|
+
// graph's external list in buildEngineConfig. It can also be
|
|
9
|
+
// declared explicitly in config.plugins — core then augments
|
|
10
|
+
// its options with configDir/wranglerConfig instead of
|
|
11
|
+
// registering it twice.
|
|
12
|
+
//
|
|
13
|
+
// Two hooks, both wired in the all-native bridge:
|
|
14
|
+
// - resolveId: the five cloudflare:* builtins from
|
|
15
|
+
// @cloudflare/vite-plugin (exact match, not a prefix) are
|
|
16
|
+
// returned as external. Belt-and-braces — the graph-list
|
|
17
|
+
// merge already externalizes them before any resolveId
|
|
18
|
+
// runs, including for dep bundling (which never yields
|
|
19
|
+
// resolveId). The hook also covers a plugin declared
|
|
20
|
+
// without the runtime, where no merge happens.
|
|
21
|
+
// - writeBundle: after the files are on disk, emit the
|
|
22
|
+
// deployment config mirroring @cloudflare/vite-plugin
|
|
23
|
+
// v1.64 (output-config.ts + deploy-config.ts): the
|
|
24
|
+
// generated <outDir>/wrangler.json, <outDir>/.dev.vars
|
|
25
|
+
// when local dev vars exist, and
|
|
26
|
+
// <configDir>/.wrangler/deploy/config.json pointing at
|
|
27
|
+
// the generated wrangler.json. Files are written with
|
|
28
|
+
// fs directly: writeBundle's this.emitFile drops assets
|
|
29
|
+
// with a warning (late emitState), so they never appear
|
|
30
|
+
// in the build's written-file list — that is expected.
|
|
31
|
+
// generateBundle is deferred in the native bridge, so
|
|
32
|
+
// writeBundle is the v1 hook; a native generateBundle
|
|
33
|
+
// yield is the clean long-term fix.
|
|
34
|
+
//
|
|
35
|
+
// The wrangler file is the single source of truth for the
|
|
36
|
+
// deployment config (SPEC §16.3): it is read with the
|
|
37
|
+
// package's existing readWranglerConfig (wrangler's
|
|
38
|
+
// unstable_readConfig), never duplicated in jundle config.
|
|
39
|
+
// Top-level/default env only (named env.* is out of scope,
|
|
40
|
+
// SPEC decisions O10).
|
|
41
|
+
import fs from "node:fs";
|
|
42
|
+
import path from "node:path";
|
|
43
|
+
import { createUnplugin } from "unplugin";
|
|
44
|
+
import { readWranglerConfig } from "./config.js";
|
|
45
|
+
|
|
46
|
+
// The Workers runtime builtins, exactly
|
|
47
|
+
// @cloudflare/vite-plugin's cloudflareBuiltInModules
|
|
48
|
+
// (v1.64.0, src/plugins/output-config.ts). Exact match
|
|
49
|
+
// only: "cloudflare:*" as a pattern would never match
|
|
50
|
+
// the engine's exact-or-pat/ external check, and a
|
|
51
|
+
// prefix match here would externalize future unknown
|
|
52
|
+
// subpaths. Core merges this same list into every
|
|
53
|
+
// graph's external list (build.js buildEngineConfig);
|
|
54
|
+
// keep the two in sync — the test asserts they match.
|
|
55
|
+
export const CLOUDFLARE_BUILTINS = [
|
|
56
|
+
"cloudflare:email",
|
|
57
|
+
"cloudflare:node",
|
|
58
|
+
"cloudflare:sockets",
|
|
59
|
+
"cloudflare:workers",
|
|
60
|
+
"cloudflare:workflows",
|
|
61
|
+
];
|
|
62
|
+
const BUILTIN_RE = /^cloudflare:(email|node|sockets|workers|workflows)$/;
|
|
63
|
+
|
|
64
|
+
// Quote a value so wrangler reads it back unchanged when
|
|
65
|
+
// it parses the emitted .dev.vars (upstream
|
|
66
|
+
// quoteForDotenv, output-config.ts): pick the first quote
|
|
67
|
+
// character that does not appear in the value (single,
|
|
68
|
+
// then backtick, then double). Throw rather than silently
|
|
69
|
+
// corrupt a value that cannot be losslessly serialized.
|
|
70
|
+
export function quoteForDotenv(value) {
|
|
71
|
+
if (!value.includes("'")) return `'${value}'`;
|
|
72
|
+
if (!value.includes("`")) return `\`${value}\``;
|
|
73
|
+
if (!value.includes('"') && !/[\\\n\r]/.test(value)) return `"${value}"`;
|
|
74
|
+
throw new Error(
|
|
75
|
+
"CF plugin: unable to serialize .dev.vars value: contains every supported quote character or unsafe escape sequence",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function serializeDevVars(devVars) {
|
|
80
|
+
return Object.entries(devVars || {})
|
|
81
|
+
.map(([key, value]) => `${key}=${quoteForDotenv(String(value ?? ""))}\n`)
|
|
82
|
+
.join("");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Rebase a source-relative path (resolved against the
|
|
86
|
+
// wrangler file's directory) to be relative to the
|
|
87
|
+
// output directory (upstream rebasePathToOutput). "." when
|
|
88
|
+
// the two directories coincide. Always posix separators:
|
|
89
|
+
// wrangler.json paths are posix.
|
|
90
|
+
function rebasePathToOutput(sourcePath, sourceConfigDir, outputDir) {
|
|
91
|
+
const rel = path.relative(outputDir, path.resolve(sourceConfigDir, sourcePath));
|
|
92
|
+
return rel.split(path.sep).join("/") || ".";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Upstream rewriteMigrationsPattern (output-config.ts),
|
|
96
|
+
// ported verbatim: strip the source migrations dir prefix
|
|
97
|
+
// from the pattern and re-root it under the output
|
|
98
|
+
// migrations dir. Undefined stays undefined (the key is
|
|
99
|
+
// then omitted from the emitted config).
|
|
100
|
+
export function rewriteMigrationsPattern(migrationsPattern, fromMigrationsDir, toMigrationsDir) {
|
|
101
|
+
if (migrationsPattern === undefined) return undefined;
|
|
102
|
+
const normalizedDir = path.posix.normalize(fromMigrationsDir.split(path.sep).join("/"));
|
|
103
|
+
const normalizedPattern = path.posix.normalize(migrationsPattern.split(path.sep).join("/"));
|
|
104
|
+
const suffix =
|
|
105
|
+
normalizedDir === "."
|
|
106
|
+
? normalizedPattern
|
|
107
|
+
: path.posix.relative(normalizedDir, normalizedPattern);
|
|
108
|
+
return path.posix.normalize(toMigrationsDir === "." ? suffix : `${toMigrationsDir}/${suffix}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// The generated <outDir>/wrangler.json (upstream
|
|
112
|
+
// getOutputConfig, output-config.ts): the user's wrangler
|
|
113
|
+
// config, spread first, then the build overrides. Pure
|
|
114
|
+
// function, exported for tests.
|
|
115
|
+
// - main: the server entry chunk, outDir-relative (e.g.
|
|
116
|
+
// "server/server.js").
|
|
117
|
+
// - no_bundle: true (the chunk is already bundled).
|
|
118
|
+
// - rules: ESModule for all JS/MJS.
|
|
119
|
+
// - assets: only when the build has client output — the
|
|
120
|
+
// built client directory is always "client" under the
|
|
121
|
+
// outDir (emit.rs maps react-client -> client), so the
|
|
122
|
+
// directory is "client" regardless of what the user's
|
|
123
|
+
// assets said (a string assets value is normalised to
|
|
124
|
+
// object form first, as bindings.js does for dev).
|
|
125
|
+
// - containers: dockerfile/build_context rebased to the
|
|
126
|
+
// output dir, build_vars passed through. (Containers are
|
|
127
|
+
// out of scope for the dev runner, SPEC §16.3, but a
|
|
128
|
+
// verbatim passthrough would leave stale source-relative
|
|
129
|
+
// paths in a deployed config, so they are rebased.)
|
|
130
|
+
// - d1_databases: migrations_dir defaults to "migrations"
|
|
131
|
+
// and is rebased; migrations_pattern is rewritten.
|
|
132
|
+
// - upload_source_maps: true only when the user did not
|
|
133
|
+
// set it and the entry chunk has a .map in the bundle
|
|
134
|
+
// (builds always emit sourcemaps, build.js hardcodes
|
|
135
|
+
// sourcemap: true — the bundle-list check is the
|
|
136
|
+
// evidence, not the assumption).
|
|
137
|
+
// - configPath (the source file pointer, if
|
|
138
|
+
// unstable_readConfig returned one) is stripped: it
|
|
139
|
+
// describes the source file, never the output.
|
|
140
|
+
// - An empty unsafe: {} is deleted (upstream).
|
|
141
|
+
// Every other field (name, compatibility_date/flags,
|
|
142
|
+
// vars, bindings) passes through verbatim.
|
|
143
|
+
export function getOutputConfig({ userConfig, main, includeAssets, outDir, configDir, sourcemaps }) {
|
|
144
|
+
// unstable_readConfig returns an absolute configPath;
|
|
145
|
+
// resolve a relative one against configDir so the
|
|
146
|
+
// rebase base is never accidentally process.cwd().
|
|
147
|
+
const sourceConfigDir = userConfig.configPath
|
|
148
|
+
? path.dirname(path.resolve(configDir, userConfig.configPath))
|
|
149
|
+
: configDir;
|
|
150
|
+
const rebase = (p) => rebasePathToOutput(p, sourceConfigDir, outDir);
|
|
151
|
+
const out = {
|
|
152
|
+
...userConfig,
|
|
153
|
+
main,
|
|
154
|
+
no_bundle: true,
|
|
155
|
+
rules: [{ type: "ESModule", globs: ["**/*.js", "**/*.mjs"] }],
|
|
156
|
+
};
|
|
157
|
+
delete out.configPath;
|
|
158
|
+
if (includeAssets) {
|
|
159
|
+
const assets =
|
|
160
|
+
userConfig.assets && typeof userConfig.assets === "object" ? userConfig.assets : {};
|
|
161
|
+
out.assets = { ...assets, directory: "client" };
|
|
162
|
+
} else {
|
|
163
|
+
delete out.assets;
|
|
164
|
+
}
|
|
165
|
+
if (Array.isArray(userConfig.containers)) {
|
|
166
|
+
out.containers = userConfig.containers.map((container) => {
|
|
167
|
+
if (container.images === undefined) return container;
|
|
168
|
+
return {
|
|
169
|
+
...container,
|
|
170
|
+
images: Object.fromEntries(
|
|
171
|
+
Object.entries(container.images).map(([name, image]) => {
|
|
172
|
+
if (image.dockerfile === undefined) return [name, image];
|
|
173
|
+
return [
|
|
174
|
+
name,
|
|
175
|
+
{
|
|
176
|
+
...image,
|
|
177
|
+
dockerfile: rebase(image.dockerfile),
|
|
178
|
+
...(image.build_context === undefined
|
|
179
|
+
? {}
|
|
180
|
+
: { build_context: rebase(image.build_context) }),
|
|
181
|
+
...(image.build_vars === undefined ? {} : { build_vars: image.build_vars }),
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
}),
|
|
185
|
+
),
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (Array.isArray(userConfig.d1_databases)) {
|
|
190
|
+
out.d1_databases = userConfig.d1_databases.map((database) => {
|
|
191
|
+
const sourceMigrationsDir = database.migrations_dir ?? "migrations";
|
|
192
|
+
const outputMigrationsDir = rebase(sourceMigrationsDir);
|
|
193
|
+
const pattern = rewriteMigrationsPattern(
|
|
194
|
+
database.migrations_pattern,
|
|
195
|
+
sourceMigrationsDir,
|
|
196
|
+
outputMigrationsDir,
|
|
197
|
+
);
|
|
198
|
+
return {
|
|
199
|
+
...database,
|
|
200
|
+
migrations_dir: outputMigrationsDir,
|
|
201
|
+
...(pattern === undefined ? {} : { migrations_pattern: pattern }),
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (userConfig.upload_source_maps === undefined && sourcemaps) {
|
|
206
|
+
out.upload_source_maps = true;
|
|
207
|
+
}
|
|
208
|
+
if (out.unsafe && typeof out.unsafe === "object" && Object.keys(out.unsafe).length === 0) {
|
|
209
|
+
delete out.unsafe;
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// The deploy redirect (upstream writeDeployConfig,
|
|
215
|
+
// deploy-config.ts): <configDir>/.wrangler/deploy/
|
|
216
|
+
// config.json, whose configPath is relative from the
|
|
217
|
+
// deploy dir to the generated wrangler.json. wrangler
|
|
218
|
+
// deploy reads the GENERATED config, not the source
|
|
219
|
+
// wrangler file. Single-worker jundle has no auxiliary
|
|
220
|
+
// or prerender workers, so those are [] / omitted.
|
|
221
|
+
export function buildDeployConfig({ configDir, outDir }) {
|
|
222
|
+
const deployDir = path.join(configDir, ".wrangler", "deploy");
|
|
223
|
+
return {
|
|
224
|
+
configPath: path
|
|
225
|
+
.relative(deployDir, path.join(outDir, "wrangler.json"))
|
|
226
|
+
.split(path.sep)
|
|
227
|
+
.join("/"),
|
|
228
|
+
auxiliaryWorkers: [],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Find the Worker entry in server-manifest.json: the
|
|
233
|
+
// entry carrying runtime "@jundle/cloudflare". Fallback
|
|
234
|
+
// is the first entry (the process entry) — for a plugin
|
|
235
|
+
// declared explicitly without a CF runtime entry, the
|
|
236
|
+
// main is still the entry chunk; only a manifest with no
|
|
237
|
+
// entries at all is a hard error.
|
|
238
|
+
function getCloudflareEntry(manifest) {
|
|
239
|
+
const entries = Array.isArray(manifest?.entries) ? manifest.entries : [];
|
|
240
|
+
if (!entries.length) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
"CF plugin: server-manifest.json has no entries; cannot determine the Worker entry chunk for wrangler.json",
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
return entries.find((e) => e.runtime === "@jundle/cloudflare") || entries[0];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// The build plugin (unplugin factory, as the demo plugin
|
|
249
|
+
// in examples/rsc-app/plugins/demo-plugin.js). Options
|
|
250
|
+
// are core-managed when auto-registered: { configDir,
|
|
251
|
+
// wranglerConfig? }. A user declaring the plugin
|
|
252
|
+
// explicitly passes the same options; configDir is
|
|
253
|
+
// required for the wrangler output (the writeBundle
|
|
254
|
+
// handler only receives { dir } + the file list, never
|
|
255
|
+
// configDir — so it must be captured here, in the
|
|
256
|
+
// closure, at construction time).
|
|
257
|
+
const cloudflareBuildPlugin = createUnplugin((options = {}) => {
|
|
258
|
+
const configDir = options.configDir;
|
|
259
|
+
const wranglerConfig = options.wranglerConfig ?? null;
|
|
260
|
+
return {
|
|
261
|
+
name: "@jundle/cloudflare",
|
|
262
|
+
enforce: "pre",
|
|
263
|
+
resolveId(source) {
|
|
264
|
+
if (BUILTIN_RE.test(source)) return { id: source, external: true };
|
|
265
|
+
return null;
|
|
266
|
+
},
|
|
267
|
+
async writeBundle(writeOptions, bundle) {
|
|
268
|
+
if (!configDir || typeof configDir !== "string") {
|
|
269
|
+
throw new Error(
|
|
270
|
+
"CF plugin: writeBundle needs the project directory (configDir); it is passed by jundle core when the plugin is auto-registered for a @jundle/cloudflare runtime entry, or as the plugin option { configDir } when declared explicitly",
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const outDir = writeOptions?.dir ? path.resolve(writeOptions.dir) : null;
|
|
274
|
+
if (!outDir) {
|
|
275
|
+
throw new Error("CF plugin: writeBundle received no output directory (options.dir)");
|
|
276
|
+
}
|
|
277
|
+
const manifestPath = path.join(outDir, "server-manifest.json");
|
|
278
|
+
let manifest;
|
|
279
|
+
try {
|
|
280
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
281
|
+
} catch (e) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`CF plugin: cannot read ${manifestPath} for the wrangler output (load error: ${e.message})`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const entry = getCloudflareEntry(manifest);
|
|
287
|
+
const main = entry.chunk;
|
|
288
|
+
if (!main || typeof main !== "string") {
|
|
289
|
+
throw new Error(
|
|
290
|
+
`CF plugin: server-manifest.json entry ${JSON.stringify(entry.id ?? entry.file)} has no chunk path; cannot set wrangler.json main`,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
const { config: userConfig, devVars } = await readWranglerConfig({
|
|
294
|
+
configDir,
|
|
295
|
+
wranglerConfig,
|
|
296
|
+
});
|
|
297
|
+
const files = Array.isArray(bundle) ? bundle : [];
|
|
298
|
+
const includeAssets =
|
|
299
|
+
files.some((f) => f === "client" || f.startsWith("client/")) ||
|
|
300
|
+
fs.existsSync(path.join(outDir, "client"));
|
|
301
|
+
const sourcemaps = files.includes(`${main}.map`);
|
|
302
|
+
const outputConfig = getOutputConfig({
|
|
303
|
+
userConfig,
|
|
304
|
+
main,
|
|
305
|
+
includeAssets,
|
|
306
|
+
outDir,
|
|
307
|
+
configDir,
|
|
308
|
+
sourcemaps,
|
|
309
|
+
});
|
|
310
|
+
fs.writeFileSync(
|
|
311
|
+
path.join(outDir, "wrangler.json"),
|
|
312
|
+
`${JSON.stringify(outputConfig, null, 2)}\n`,
|
|
313
|
+
);
|
|
314
|
+
if (devVars && Object.keys(devVars).length > 0) {
|
|
315
|
+
fs.writeFileSync(path.join(outDir, ".dev.vars"), serializeDevVars(devVars));
|
|
316
|
+
}
|
|
317
|
+
const deployDir = path.join(configDir, ".wrangler", "deploy");
|
|
318
|
+
fs.mkdirSync(deployDir, { recursive: true });
|
|
319
|
+
fs.writeFileSync(
|
|
320
|
+
path.join(deployDir, "config.json"),
|
|
321
|
+
`${JSON.stringify(buildDeployConfig({ configDir, outDir }), null, 2)}\n`,
|
|
322
|
+
);
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
export default cloudflareBuildPlugin;
|
package/src/config.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Wrangler config reading for the CF plugin (SPEC §16.3).
|
|
2
|
+
// The single jundle-side setting is which wrangler config file
|
|
3
|
+
// to use (dev.environments.<env>.wranglerConfig, relative to
|
|
4
|
+
// the jundle config dir). Everything else comes from the
|
|
5
|
+
// wrangler file on disk — nothing is duplicated in jundle
|
|
6
|
+
// config. If wranglerConfig is omitted, auto-discover from the
|
|
7
|
+
// project root (the jundle config dir) in order wrangler.jsonc,
|
|
8
|
+
// wrangler.json, wrangler.toml. If workerd is selected and no
|
|
9
|
+
// wrangler file is found, a clear error names the missing file.
|
|
10
|
+
// Config is read via wrangler's own utilities
|
|
11
|
+
// (unstable_readConfig); the wrangler version is pinned in
|
|
12
|
+
// this package's dependencies because the `unstable_` prefix
|
|
13
|
+
// means the API can change across versions.
|
|
14
|
+
// Secrets: wrangler `vars` + `.dev.vars` (read from disk
|
|
15
|
+
// alongside the wrangler file, falling back to the project
|
|
16
|
+
// root). `.dev.vars` is parsed as a dotenv subset (KEY=VALUE
|
|
17
|
+
// lines, # comments, surrounding quotes stripped); wrangler's
|
|
18
|
+
// own reader is not exported, so this is jundle's parser.
|
|
19
|
+
import fs from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { unstable_readConfig } from "wrangler";
|
|
22
|
+
|
|
23
|
+
const DISCOVER_ORDER = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
24
|
+
|
|
25
|
+
export function resolveWranglerFile({ configDir, wranglerConfig }) {
|
|
26
|
+
if (wranglerConfig) {
|
|
27
|
+
const file = path.resolve(configDir, wranglerConfig);
|
|
28
|
+
if (!fs.existsSync(file)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`CF plugin: wrangler config file not found: ${file} (dev.environments.<env>.wranglerConfig)`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return file;
|
|
34
|
+
}
|
|
35
|
+
for (const name of DISCOVER_ORDER) {
|
|
36
|
+
const file = path.join(configDir, name);
|
|
37
|
+
if (fs.existsSync(file)) return file;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(
|
|
40
|
+
`CF plugin: no wrangler config file found (looked for ${DISCOVER_ORDER.join(", ")} in ${configDir}). ` +
|
|
41
|
+
`Set dev.environments.<env>.wranglerConfig to the wrangler config file to use.`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function parseDevVars(text) {
|
|
46
|
+
const vars = {};
|
|
47
|
+
for (const line of text.split("\n")) {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
50
|
+
const eq = trimmed.indexOf("=");
|
|
51
|
+
if (eq === -1) continue;
|
|
52
|
+
const key = trimmed.slice(0, eq).trim();
|
|
53
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
54
|
+
if (
|
|
55
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
56
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
57
|
+
) {
|
|
58
|
+
value = value.slice(1, -1);
|
|
59
|
+
}
|
|
60
|
+
if (key) vars[key] = value;
|
|
61
|
+
}
|
|
62
|
+
return vars;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function readDevVars({ wranglerFile, configDir }) {
|
|
66
|
+
// Alongside the wrangler file first, then the project root.
|
|
67
|
+
const candidates = [
|
|
68
|
+
path.join(path.dirname(wranglerFile), ".dev.vars"),
|
|
69
|
+
path.join(configDir, ".dev.vars"),
|
|
70
|
+
];
|
|
71
|
+
for (const file of candidates) {
|
|
72
|
+
if (fs.existsSync(file)) return parseDevVars(fs.readFileSync(file, "utf8"));
|
|
73
|
+
}
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function readWranglerConfig({ configDir, wranglerConfig }) {
|
|
78
|
+
const file = resolveWranglerFile({ configDir, wranglerConfig });
|
|
79
|
+
const config = await unstable_readConfig({ config: file });
|
|
80
|
+
const devVars = readDevVars({ wranglerFile: file, configDir });
|
|
81
|
+
return { file, config, devVars };
|
|
82
|
+
}
|