devflare 1.0.0-next.22 → 1.0.0-next.23
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/LLM.md +144 -5
- package/dist/account-j8nfggg4.js +475 -0
- package/dist/account-qhe8vtds.js +475 -0
- package/dist/bridge/miniflare.d.ts +1 -1
- package/dist/bridge/miniflare.d.ts.map +1 -1
- package/dist/browser.d.ts +13 -13
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -2
- package/dist/build-vy95gy3f.js +54 -0
- package/dist/build-yzx0gsaj.js +54 -0
- package/dist/cli/commands/build-artifacts.d.ts.map +1 -1
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/type-generation/generator.d.ts +4 -2
- package/dist/cli/commands/type-generation/generator.d.ts.map +1 -1
- package/dist/cli/commands/types.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/config/compiler/types.d.ts +1 -1
- package/dist/config/compiler/types.d.ts.map +1 -1
- package/dist/config/define.d.ts +7 -4
- package/dist/config/define.d.ts.map +1 -1
- package/dist/config/env-vars.d.ts +309 -0
- package/dist/config/env-vars.d.ts.map +1 -0
- package/dist/config/index.d.ts +2 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/local-dev-vars.d.ts +2 -2
- package/dist/config/local-dev-vars.d.ts.map +1 -1
- package/dist/config/schema-env.d.ts +6 -6
- package/dist/config/schema-types-bindings-platform.d.ts +378 -0
- package/dist/config/schema-types-bindings-platform.d.ts.map +1 -0
- package/dist/config/schema-types-bindings-resources.d.ts +551 -0
- package/dist/config/schema-types-bindings-resources.d.ts.map +1 -0
- package/dist/config/schema-types-bindings.d.ts +254 -0
- package/dist/config/schema-types-bindings.d.ts.map +1 -0
- package/dist/config/schema-types-build.d.ts +86 -0
- package/dist/config/schema-types-build.d.ts.map +1 -0
- package/dist/config/schema-types-runtime.d.ts +882 -0
- package/dist/config/schema-types-runtime.d.ts.map +1 -0
- package/dist/config/schema-types.d.ts +377 -0
- package/dist/config/schema-types.d.ts.map +1 -0
- package/dist/config/schema.d.ts +14 -15
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config-entry.d.ts +2 -0
- package/dist/config-entry.d.ts.map +1 -1
- package/dist/config-entry.js +3 -1
- package/dist/config-gq5jh4cx.js +105 -0
- package/dist/config-vec13050.js +105 -0
- package/dist/deploy-01j0ep5n.js +1055 -0
- package/dist/deploy-tjypkhg7.js +1055 -0
- package/dist/dev-bh581ew3.js +2597 -0
- package/dist/dev-gn5y93z9.js +2597 -0
- package/dist/dev-server/server.d.ts.map +1 -1
- package/dist/doctor-h5q28qt1.js +259 -0
- package/dist/doctor-khk550tw.js +259 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/index-0bv2qjs1.js +1555 -0
- package/dist/index-3tkzn06q.js +413 -0
- package/dist/index-8fyz6gcm.js +699 -0
- package/dist/index-97z629zr.js +109 -0
- package/dist/index-b28c4yr4.js +1205 -0
- package/dist/index-c8p4njqy.js +479 -0
- package/dist/index-cr06zrgw.js +1033 -0
- package/dist/index-cwjjdtgn.js +74 -0
- package/dist/index-dref9ecb.js +476 -0
- package/dist/index-e151t4ge.js +895 -0
- package/dist/index-e7kakw0j.js +1033 -0
- package/dist/index-f1g5jdm8.js +1426 -0
- package/dist/index-f46984zs.js +1554 -0
- package/dist/index-grk8pzhr.js +185 -0
- package/dist/index-hzmpecq9.js +52 -0
- package/dist/index-j1csb7gb.js +581 -0
- package/dist/index-j7x7f72h.js +185 -0
- package/dist/index-jkqbjwt2.js +476 -0
- package/dist/index-mh5renra.js +895 -0
- package/dist/index-p9xq83p7.js +147 -0
- package/dist/index-q15nj71j.js +52 -0
- package/dist/index-qqp65pyv.js +699 -0
- package/dist/index-s0fmwxbk.js +74 -0
- package/dist/index-stzx8nc4.js +111 -0
- package/dist/index-th4vrnbk.js +1205 -0
- package/dist/index-vtcmsgaf.js +581 -0
- package/dist/index-x2k3awjs.js +147 -0
- package/dist/index-x8x547tz.js +1426 -0
- package/dist/index-xxxd0mvw.js +109 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/login-280p2cm9.js +77 -0
- package/dist/login-4n266whq.js +77 -0
- package/dist/previews-3m3ffpaw.js +1337 -0
- package/dist/previews-tr8sm03d.js +1337 -0
- package/dist/productions-62y489ff.js +505 -0
- package/dist/productions-cgn3fz7d.js +505 -0
- package/dist/runtime/exports.d.ts +23 -0
- package/dist/runtime/exports.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +5 -3
- package/dist/secrets-4050kqf5.js +91 -0
- package/dist/secrets-p112cajt.js +91 -0
- package/dist/sveltekit/index.js +4 -4
- package/dist/test/index.js +23 -11
- package/dist/test/resolve-service-bindings.d.ts +1 -1
- package/dist/test/resolve-service-bindings.d.ts.map +1 -1
- package/dist/test/simple-context-lifecycle.d.ts.map +1 -1
- package/dist/types-apmt10yj.js +705 -0
- package/dist/types-ttrrgdfj.js +705 -0
- package/dist/vite/index.js +5 -5
- package/dist/vite/plugin-context.d.ts.map +1 -1
- package/dist/vite/plugin-programmatic.d.ts.map +1 -1
- package/dist/worker-2k1jyr6p.js +513 -0
- package/dist/worker-jqgn6jyj.js +513 -0
- package/package.json +1 -1
|
@@ -0,0 +1,895 @@
|
|
|
1
|
+
import {
|
|
2
|
+
discoverDurableObjectFiles
|
|
3
|
+
} from "./index-cr06zrgw.js";
|
|
4
|
+
import {
|
|
5
|
+
findFiles
|
|
6
|
+
} from "./index-qwgr4q7s.js";
|
|
7
|
+
import {
|
|
8
|
+
transformDurableObject
|
|
9
|
+
} from "./index-vhqww6tt.js";
|
|
10
|
+
import {
|
|
11
|
+
__require
|
|
12
|
+
} from "./index-37x76zdn.js";
|
|
13
|
+
|
|
14
|
+
// src/bundler/worker-bundler.ts
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { dirname, resolve as resolve2 } from "pathe";
|
|
17
|
+
|
|
18
|
+
// src/bundler/defaults.ts
|
|
19
|
+
function createWorkerdBundlerDefaults() {
|
|
20
|
+
return {
|
|
21
|
+
platform: "browser",
|
|
22
|
+
defaultTsconfigMode: "if-present",
|
|
23
|
+
sourcemap: false,
|
|
24
|
+
minify: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/bundler/rolldown-shared.ts
|
|
29
|
+
import { existsSync } from "node:fs";
|
|
30
|
+
import { resolve } from "pathe";
|
|
31
|
+
|
|
32
|
+
// src/bundler/worker-compat.ts
|
|
33
|
+
import MagicString from "magic-string";
|
|
34
|
+
import ts from "typescript";
|
|
35
|
+
var WORKER_DYNAMIC_IMPORT_ERROR = "Devflare worker bundles cannot contain unresolved dynamic import() expressions because Miniflare/workerd reject dynamic module specifiers";
|
|
36
|
+
function getScriptKind(id) {
|
|
37
|
+
if (id.endsWith(".tsx")) {
|
|
38
|
+
return ts.ScriptKind.TSX;
|
|
39
|
+
}
|
|
40
|
+
if (id.endsWith(".ts") || id.endsWith(".mts") || id.endsWith(".cts")) {
|
|
41
|
+
return ts.ScriptKind.TS;
|
|
42
|
+
}
|
|
43
|
+
if (id.endsWith(".jsx")) {
|
|
44
|
+
return ts.ScriptKind.JSX;
|
|
45
|
+
}
|
|
46
|
+
return ts.ScriptKind.JS;
|
|
47
|
+
}
|
|
48
|
+
function createSourceFile(code, id) {
|
|
49
|
+
return ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true, getScriptKind(id));
|
|
50
|
+
}
|
|
51
|
+
function hasExportModifier(node) {
|
|
52
|
+
if (!ts.canHaveModifiers(node)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
56
|
+
}
|
|
57
|
+
function isConstVariableStatement(statement) {
|
|
58
|
+
return ts.isVariableStatement(statement) && (statement.declarationList.flags & ts.NodeFlags.Const) !== 0;
|
|
59
|
+
}
|
|
60
|
+
function quoteString(value) {
|
|
61
|
+
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, `\\'`)}'`;
|
|
62
|
+
}
|
|
63
|
+
function resolveLiteralImportSpecifier(expression, constStrings) {
|
|
64
|
+
if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) {
|
|
65
|
+
return expression.text;
|
|
66
|
+
}
|
|
67
|
+
if (ts.isParenthesizedExpression(expression)) {
|
|
68
|
+
return resolveLiteralImportSpecifier(expression.expression, constStrings);
|
|
69
|
+
}
|
|
70
|
+
if (ts.isIdentifier(expression)) {
|
|
71
|
+
return constStrings.get(expression.text) ?? null;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function collectTopLevelConstStrings(sourceFile) {
|
|
76
|
+
const constStrings = new Map;
|
|
77
|
+
for (const statement of sourceFile.statements) {
|
|
78
|
+
if (!isConstVariableStatement(statement)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
82
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const literal = resolveLiteralImportSpecifier(declaration.initializer, constStrings);
|
|
86
|
+
if (literal !== null) {
|
|
87
|
+
constStrings.set(declaration.name.text, literal);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return constStrings;
|
|
92
|
+
}
|
|
93
|
+
function unwrapDynamicImportExpression(expression) {
|
|
94
|
+
if (ts.isParenthesizedExpression(expression)) {
|
|
95
|
+
return unwrapDynamicImportExpression(expression.expression);
|
|
96
|
+
}
|
|
97
|
+
if (ts.isAwaitExpression(expression)) {
|
|
98
|
+
return unwrapDynamicImportExpression(expression.expression);
|
|
99
|
+
}
|
|
100
|
+
if (!ts.isCallExpression(expression) || expression.expression.kind !== ts.SyntaxKind.ImportKeyword) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return expression;
|
|
104
|
+
}
|
|
105
|
+
function getWrapperBodyExpression(body) {
|
|
106
|
+
if (!body) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (!ts.isBlock(body)) {
|
|
110
|
+
return body;
|
|
111
|
+
}
|
|
112
|
+
if (body.statements.length !== 1) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const [statement] = body.statements;
|
|
116
|
+
if (!ts.isReturnStatement(statement) || !statement.expression) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
return statement.expression;
|
|
120
|
+
}
|
|
121
|
+
function createImportWrapper(name, parameterName, body, sourceFile) {
|
|
122
|
+
const bodyExpression = getWrapperBodyExpression(body);
|
|
123
|
+
if (!bodyExpression) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const importExpression = unwrapDynamicImportExpression(bodyExpression);
|
|
127
|
+
if (!importExpression || importExpression.arguments.length < 1) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const [importArgument] = importExpression.arguments;
|
|
131
|
+
if (!ts.isIdentifier(importArgument) || importArgument.text !== parameterName || !body) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
name,
|
|
136
|
+
parameterName,
|
|
137
|
+
bodyStart: body.getStart(sourceFile),
|
|
138
|
+
bodyEnd: body.getEnd(),
|
|
139
|
+
specifiers: new Set,
|
|
140
|
+
hasUnsupportedCalls: false
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function collectImportWrappers(sourceFile) {
|
|
144
|
+
const wrappers = new Map;
|
|
145
|
+
for (const statement of sourceFile.statements) {
|
|
146
|
+
if (ts.isFunctionDeclaration(statement) && statement.name && !hasExportModifier(statement) && statement.parameters.length === 1 && ts.isIdentifier(statement.parameters[0]?.name)) {
|
|
147
|
+
const wrapper = createImportWrapper(statement.name.text, statement.parameters[0].name.text, statement.body, sourceFile);
|
|
148
|
+
if (wrapper) {
|
|
149
|
+
wrappers.set(wrapper.name, wrapper);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (!isConstVariableStatement(statement) || hasExportModifier(statement)) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
156
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (!ts.isArrowFunction(declaration.initializer) && !ts.isFunctionExpression(declaration.initializer)) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (declaration.initializer.parameters.length !== 1 || !ts.isIdentifier(declaration.initializer.parameters[0]?.name)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const wrapper = createImportWrapper(declaration.name.text, declaration.initializer.parameters[0].name.text, declaration.initializer.body, sourceFile);
|
|
166
|
+
if (wrapper) {
|
|
167
|
+
wrappers.set(wrapper.name, wrapper);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return wrappers;
|
|
172
|
+
}
|
|
173
|
+
function collectWrapperCallSpecifiers(sourceFile, wrappers, constStrings) {
|
|
174
|
+
function visit(node) {
|
|
175
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
176
|
+
const wrapper = wrappers.get(node.expression.text);
|
|
177
|
+
if (wrapper) {
|
|
178
|
+
if (node.arguments.length !== 1) {
|
|
179
|
+
wrapper.hasUnsupportedCalls = true;
|
|
180
|
+
} else {
|
|
181
|
+
const specifier = resolveLiteralImportSpecifier(node.arguments[0], constStrings);
|
|
182
|
+
if (specifier === null) {
|
|
183
|
+
wrapper.hasUnsupportedCalls = true;
|
|
184
|
+
} else {
|
|
185
|
+
wrapper.specifiers.add(specifier);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
ts.forEachChild(node, visit);
|
|
191
|
+
}
|
|
192
|
+
visit(sourceFile);
|
|
193
|
+
}
|
|
194
|
+
function createImportIdentifier(specifierToIdentifier, specifier) {
|
|
195
|
+
const existing = specifierToIdentifier.get(specifier);
|
|
196
|
+
if (existing) {
|
|
197
|
+
return existing;
|
|
198
|
+
}
|
|
199
|
+
const identifier = `__devflareDynamicImport${specifierToIdentifier.size}`;
|
|
200
|
+
specifierToIdentifier.set(specifier, identifier);
|
|
201
|
+
return identifier;
|
|
202
|
+
}
|
|
203
|
+
function buildWrapperBody(wrapper, specifierToIdentifier) {
|
|
204
|
+
const cases = Array.from(wrapper.specifiers).sort((left, right) => left.localeCompare(right)).map((specifier) => {
|
|
205
|
+
const identifier = createImportIdentifier(specifierToIdentifier, specifier);
|
|
206
|
+
return ` case ${quoteString(specifier)}:
|
|
207
|
+
return Promise.resolve(${identifier})`;
|
|
208
|
+
}).join(`
|
|
209
|
+
`);
|
|
210
|
+
return `{
|
|
211
|
+
switch (${wrapper.parameterName}) {
|
|
212
|
+
${cases}
|
|
213
|
+
default:
|
|
214
|
+
return Promise.reject(new Error(\`Unsupported dynamic import in Devflare worker bundle: \${${wrapper.parameterName}}\`))
|
|
215
|
+
}
|
|
216
|
+
}`;
|
|
217
|
+
}
|
|
218
|
+
function findContainingWrapper(callExpression, wrappers, sourceFile) {
|
|
219
|
+
const start = callExpression.getStart(sourceFile);
|
|
220
|
+
const end = callExpression.getEnd();
|
|
221
|
+
for (const wrapper of wrappers) {
|
|
222
|
+
if (start >= wrapper.bodyStart && end <= wrapper.bodyEnd) {
|
|
223
|
+
return wrapper;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
function transformWorkerDynamicImports(code, id) {
|
|
229
|
+
const sourceFile = createSourceFile(code, id);
|
|
230
|
+
const constStrings = collectTopLevelConstStrings(sourceFile);
|
|
231
|
+
const wrappers = collectImportWrappers(sourceFile);
|
|
232
|
+
collectWrapperCallSpecifiers(sourceFile, wrappers, constStrings);
|
|
233
|
+
const transformableWrappers = Array.from(wrappers.values()).filter((wrapper) => {
|
|
234
|
+
return !wrapper.hasUnsupportedCalls && wrapper.specifiers.size > 0;
|
|
235
|
+
});
|
|
236
|
+
const replacements = [];
|
|
237
|
+
const specifierToIdentifier = new Map;
|
|
238
|
+
function visit(node) {
|
|
239
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
240
|
+
const containingWrapper = findContainingWrapper(node, transformableWrappers, sourceFile);
|
|
241
|
+
if (containingWrapper && node.arguments.length === 1 && ts.isIdentifier(node.arguments[0]) && node.arguments[0].text === containingWrapper.parameterName) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const [argument] = node.arguments;
|
|
245
|
+
if (!argument) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const specifier = resolveLiteralImportSpecifier(argument, constStrings);
|
|
249
|
+
if (specifier !== null) {
|
|
250
|
+
replacements.push({
|
|
251
|
+
start: node.getStart(sourceFile),
|
|
252
|
+
end: node.getEnd(),
|
|
253
|
+
text: `Promise.resolve(${createImportIdentifier(specifierToIdentifier, specifier)})`
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
ts.forEachChild(node, visit);
|
|
258
|
+
}
|
|
259
|
+
visit(sourceFile);
|
|
260
|
+
if (replacements.length === 0 && transformableWrappers.length === 0) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const s = new MagicString(code);
|
|
264
|
+
for (const replacement of replacements.sort((left, right) => right.start - left.start)) {
|
|
265
|
+
s.overwrite(replacement.start, replacement.end, replacement.text);
|
|
266
|
+
}
|
|
267
|
+
for (const wrapper of transformableWrappers.sort((left, right) => right.bodyStart - left.bodyStart)) {
|
|
268
|
+
s.overwrite(wrapper.bodyStart, wrapper.bodyEnd, buildWrapperBody(wrapper, specifierToIdentifier));
|
|
269
|
+
}
|
|
270
|
+
if (specifierToIdentifier.size > 0) {
|
|
271
|
+
const importBlock = Array.from(specifierToIdentifier.entries()).map(([specifier, identifier]) => `import * as ${identifier} from ${quoteString(specifier)}`).join(`
|
|
272
|
+
`);
|
|
273
|
+
if (code.startsWith("#!")) {
|
|
274
|
+
const newlineIndex = code.indexOf(`
|
|
275
|
+
`);
|
|
276
|
+
if (newlineIndex === -1) {
|
|
277
|
+
s.appendRight(code.length, `
|
|
278
|
+
${importBlock}
|
|
279
|
+
`);
|
|
280
|
+
} else {
|
|
281
|
+
s.appendLeft(newlineIndex + 1, `${importBlock}
|
|
282
|
+
`);
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
s.appendLeft(0, `${importBlock}
|
|
286
|
+
`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
code: s.toString(),
|
|
291
|
+
map: s.generateMap({
|
|
292
|
+
source: id,
|
|
293
|
+
file: `${id}.map`,
|
|
294
|
+
includeContent: true
|
|
295
|
+
})
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function findDynamicImportCalls(sourceFile) {
|
|
299
|
+
const calls = [];
|
|
300
|
+
function visit(node) {
|
|
301
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
302
|
+
calls.push(node);
|
|
303
|
+
}
|
|
304
|
+
ts.forEachChild(node, visit);
|
|
305
|
+
}
|
|
306
|
+
visit(sourceFile);
|
|
307
|
+
return calls;
|
|
308
|
+
}
|
|
309
|
+
async function assertWorkerBundleHasNoDynamicImports(bundlePath) {
|
|
310
|
+
const fs = await import("node:fs/promises");
|
|
311
|
+
const code = await fs.readFile(bundlePath, "utf-8");
|
|
312
|
+
const sourceFile = createSourceFile(code, bundlePath);
|
|
313
|
+
const dynamicImports = findDynamicImportCalls(sourceFile);
|
|
314
|
+
if (dynamicImports.length === 0) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const examples = dynamicImports.slice(0, 3).map((callExpression) => {
|
|
318
|
+
const start = callExpression.getStart(sourceFile);
|
|
319
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(start);
|
|
320
|
+
const snippet = code.slice(start, callExpression.getEnd()).replace(/\s+/g, " ").trim();
|
|
321
|
+
return `- ${line + 1}:${character + 1} ${snippet}`;
|
|
322
|
+
}).join(`
|
|
323
|
+
`);
|
|
324
|
+
throw new Error([
|
|
325
|
+
WORKER_DYNAMIC_IMPORT_ERROR,
|
|
326
|
+
`Bundle: ${bundlePath}`,
|
|
327
|
+
`Examples:
|
|
328
|
+
${examples}`,
|
|
329
|
+
"Devflare can normalize literal import() calls and simple helper wrappers whose call sites are literal strings, but truly runtime-computed specifiers must be converted to static imports for worker bundles."
|
|
330
|
+
].join(`
|
|
331
|
+
|
|
332
|
+
`));
|
|
333
|
+
}
|
|
334
|
+
function createWorkerDynamicImportPlugin() {
|
|
335
|
+
return {
|
|
336
|
+
name: "devflare-worker-dynamic-imports",
|
|
337
|
+
transform(code, id) {
|
|
338
|
+
const result = transformWorkerDynamicImports(code, id);
|
|
339
|
+
return result ?? null;
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/bundler/rolldown-shared.ts
|
|
345
|
+
var DEFAULT_EXTERNAL_MODULES = [
|
|
346
|
+
/^cloudflare:/,
|
|
347
|
+
/^node:/,
|
|
348
|
+
"buffer",
|
|
349
|
+
"crypto",
|
|
350
|
+
"events",
|
|
351
|
+
"http",
|
|
352
|
+
"https",
|
|
353
|
+
"net",
|
|
354
|
+
"os",
|
|
355
|
+
"path",
|
|
356
|
+
"stream",
|
|
357
|
+
"tls",
|
|
358
|
+
"url",
|
|
359
|
+
"util",
|
|
360
|
+
"zlib",
|
|
361
|
+
"fs",
|
|
362
|
+
"child_process",
|
|
363
|
+
"async_hooks",
|
|
364
|
+
"querystring",
|
|
365
|
+
"string_decoder",
|
|
366
|
+
"assert",
|
|
367
|
+
"dns"
|
|
368
|
+
];
|
|
369
|
+
function toArray(value) {
|
|
370
|
+
return Array.isArray(value) ? value : [value];
|
|
371
|
+
}
|
|
372
|
+
function matchesExternalPattern(pattern, id) {
|
|
373
|
+
if (pattern instanceof RegExp) {
|
|
374
|
+
pattern.lastIndex = 0;
|
|
375
|
+
return pattern.test(id);
|
|
376
|
+
}
|
|
377
|
+
return pattern === id;
|
|
378
|
+
}
|
|
379
|
+
function matchesExternalOption(option, id, parentId, isResolved) {
|
|
380
|
+
if (!option) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
if (typeof option === "function") {
|
|
384
|
+
return option(id, parentId, isResolved) ?? false;
|
|
385
|
+
}
|
|
386
|
+
return toArray(option).some((pattern) => matchesExternalPattern(pattern, id));
|
|
387
|
+
}
|
|
388
|
+
function mergeExternalOptions(base, user) {
|
|
389
|
+
if (!base) {
|
|
390
|
+
return user;
|
|
391
|
+
}
|
|
392
|
+
if (!user) {
|
|
393
|
+
return base;
|
|
394
|
+
}
|
|
395
|
+
if (typeof base !== "function" && typeof user !== "function") {
|
|
396
|
+
return [...toArray(base), ...toArray(user)];
|
|
397
|
+
}
|
|
398
|
+
return (id, parentId, isResolved) => {
|
|
399
|
+
return matchesExternalOption(base, id, parentId, isResolved) || matchesExternalOption(user, id, parentId, isResolved) || false;
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
function mergePluginOptions(base, user) {
|
|
403
|
+
if (!base) {
|
|
404
|
+
return user;
|
|
405
|
+
}
|
|
406
|
+
if (!user) {
|
|
407
|
+
return base;
|
|
408
|
+
}
|
|
409
|
+
return [base, user];
|
|
410
|
+
}
|
|
411
|
+
function aliasKey(find) {
|
|
412
|
+
return find instanceof RegExp ? `re:${find.source}:${find.flags}` : `str:${find}`;
|
|
413
|
+
}
|
|
414
|
+
function normalizeAliasEntries(input) {
|
|
415
|
+
if (!input) {
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
if (Array.isArray(input)) {
|
|
419
|
+
return input.filter((entry) => {
|
|
420
|
+
return Boolean(entry) && typeof entry.replacement === "string";
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
return Object.entries(input).filter(([, replacement]) => typeof replacement === "string").map(([find, replacement]) => ({ find, replacement }));
|
|
424
|
+
}
|
|
425
|
+
function mergeAliases(userAliases, frameworkDefaults) {
|
|
426
|
+
const userKeys = new Set(userAliases.map((entry) => aliasKey(entry.find)));
|
|
427
|
+
const frameworkFiltered = frameworkDefaults.filter((entry) => {
|
|
428
|
+
return !userKeys.has(aliasKey(entry.find));
|
|
429
|
+
});
|
|
430
|
+
const seenUserKeys = new Set;
|
|
431
|
+
const dedupedUser = [];
|
|
432
|
+
for (let index = userAliases.length - 1;index >= 0; index--) {
|
|
433
|
+
const entry = userAliases[index];
|
|
434
|
+
const key = aliasKey(entry.find);
|
|
435
|
+
if (seenUserKeys.has(key)) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
seenUserKeys.add(key);
|
|
439
|
+
dedupedUser.unshift(entry);
|
|
440
|
+
}
|
|
441
|
+
return [...frameworkFiltered, ...dedupedUser];
|
|
442
|
+
}
|
|
443
|
+
function aliasEntriesToRolldownRecord(entries) {
|
|
444
|
+
const record = {};
|
|
445
|
+
for (const entry of entries) {
|
|
446
|
+
if (entry.find instanceof RegExp) {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
record[entry.find] = entry.replacement;
|
|
450
|
+
}
|
|
451
|
+
return record;
|
|
452
|
+
}
|
|
453
|
+
function mergeResolveOptions(base, user) {
|
|
454
|
+
if (!base && !user) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const frameworkEntries = normalizeAliasEntries(base?.alias);
|
|
458
|
+
const userEntries = normalizeAliasEntries(user?.alias);
|
|
459
|
+
const mergedEntries = mergeAliases(userEntries, frameworkEntries);
|
|
460
|
+
const mergedAlias = aliasEntriesToRolldownRecord(mergedEntries);
|
|
461
|
+
return {
|
|
462
|
+
...user ?? {},
|
|
463
|
+
...base ?? {},
|
|
464
|
+
...mergedEntries.length > 0 ? { alias: mergedAlias } : {}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function resolveTsconfigOption(options) {
|
|
468
|
+
if (options.userTsconfig) {
|
|
469
|
+
return { tsconfig: options.userTsconfig };
|
|
470
|
+
}
|
|
471
|
+
const defaultTsconfigPath = resolve(options.cwd, "tsconfig.json");
|
|
472
|
+
if (options.defaultMode === "always" || existsSync(defaultTsconfigPath)) {
|
|
473
|
+
return { tsconfig: defaultTsconfigPath };
|
|
474
|
+
}
|
|
475
|
+
return {};
|
|
476
|
+
}
|
|
477
|
+
async function ensureDebugShim(outDir) {
|
|
478
|
+
const fs = await import("node:fs/promises");
|
|
479
|
+
const debugShimCode = `
|
|
480
|
+
// Debug module shim for local development
|
|
481
|
+
const createDebug = (namespace) => {
|
|
482
|
+
const logger = (...args) => {
|
|
483
|
+
if (createDebug.enabled) console.debug(\`[\${namespace}]\`, ...args)
|
|
484
|
+
}
|
|
485
|
+
logger.enabled = false
|
|
486
|
+
logger.namespace = namespace
|
|
487
|
+
logger.extend = (sub) => createDebug(\`\${namespace}:\${sub}\`)
|
|
488
|
+
return logger
|
|
489
|
+
}
|
|
490
|
+
createDebug.enabled = false
|
|
491
|
+
createDebug.formatters = {}
|
|
492
|
+
export default createDebug
|
|
493
|
+
`;
|
|
494
|
+
const debugShimPath = resolve(outDir, "_debug_shim.js");
|
|
495
|
+
await fs.writeFile(debugShimPath, debugShimCode, "utf-8");
|
|
496
|
+
return debugShimPath;
|
|
497
|
+
}
|
|
498
|
+
function resolveWorkerCompatibleRolldownConfig(options) {
|
|
499
|
+
const {
|
|
500
|
+
output: userOutputOptions,
|
|
501
|
+
input: _ignoredInput,
|
|
502
|
+
cwd: _ignoredCwd,
|
|
503
|
+
platform: _ignoredPlatform,
|
|
504
|
+
target: _ignoredTarget,
|
|
505
|
+
watch: _ignoredWatch,
|
|
506
|
+
external: userExternal,
|
|
507
|
+
plugins: userPlugins,
|
|
508
|
+
resolve: userResolve,
|
|
509
|
+
tsconfig: userTsconfig,
|
|
510
|
+
...userInputOptions
|
|
511
|
+
} = options.rolldownOptions ?? {};
|
|
512
|
+
const {
|
|
513
|
+
codeSplitting: _ignoredCodeSplitting,
|
|
514
|
+
dir: _ignoredDir,
|
|
515
|
+
file: _ignoredFile,
|
|
516
|
+
format: _ignoredFormat,
|
|
517
|
+
inlineDynamicImports: _ignoredInlineDynamicImports,
|
|
518
|
+
...safeUserOutputOptions
|
|
519
|
+
} = userOutputOptions ?? {};
|
|
520
|
+
return {
|
|
521
|
+
inputOptions: {
|
|
522
|
+
...userInputOptions,
|
|
523
|
+
input: options.inputFile,
|
|
524
|
+
cwd: options.cwd,
|
|
525
|
+
platform: options.platform,
|
|
526
|
+
...resolveTsconfigOption({
|
|
527
|
+
cwd: options.cwd,
|
|
528
|
+
userTsconfig,
|
|
529
|
+
defaultMode: options.defaultTsconfigMode
|
|
530
|
+
}),
|
|
531
|
+
external: mergeExternalOptions(DEFAULT_EXTERNAL_MODULES, userExternal),
|
|
532
|
+
plugins: mergePluginOptions(createWorkerDynamicImportPlugin(), userPlugins),
|
|
533
|
+
resolve: mergeResolveOptions(options.alias ? {
|
|
534
|
+
alias: options.alias
|
|
535
|
+
} : undefined, userResolve)
|
|
536
|
+
},
|
|
537
|
+
outputOptions: {
|
|
538
|
+
...safeUserOutputOptions,
|
|
539
|
+
file: options.outFile,
|
|
540
|
+
format: "esm",
|
|
541
|
+
sourcemap: safeUserOutputOptions.sourcemap ?? options.sourcemap ?? false,
|
|
542
|
+
minify: safeUserOutputOptions.minify ?? options.minify,
|
|
543
|
+
codeSplitting: false,
|
|
544
|
+
...options.inlineDynamicImports !== undefined ? { inlineDynamicImports: options.inlineDynamicImports } : {}
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
async function writeWorkerCompatibleBundle(options) {
|
|
549
|
+
const { rolldown } = await import("rolldown");
|
|
550
|
+
const bundle = await rolldown(options.inputOptions);
|
|
551
|
+
try {
|
|
552
|
+
await bundle.write(options.outputOptions);
|
|
553
|
+
await assertWorkerBundleHasNoDynamicImports(options.outFile);
|
|
554
|
+
} finally {
|
|
555
|
+
await bundle.close();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/bundler/worker-bundler.ts
|
|
560
|
+
async function resolveInternalModuleEntry(relativeCandidates) {
|
|
561
|
+
const fs = await import("node:fs/promises");
|
|
562
|
+
const currentFileDir = dirname(fileURLToPath(import.meta.url));
|
|
563
|
+
for (const candidate of relativeCandidates) {
|
|
564
|
+
const absolutePath = resolve2(currentFileDir, candidate);
|
|
565
|
+
try {
|
|
566
|
+
await fs.access(absolutePath);
|
|
567
|
+
return absolutePath;
|
|
568
|
+
} catch {
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
async function resolveInternalAliasMap(outDir) {
|
|
575
|
+
const debugShimPath = await ensureDebugShim(outDir);
|
|
576
|
+
const runtimeEntry = await resolveInternalModuleEntry([
|
|
577
|
+
"../runtime/index.ts",
|
|
578
|
+
"../runtime/index.js"
|
|
579
|
+
]);
|
|
580
|
+
const packageEntry = await resolveInternalModuleEntry([
|
|
581
|
+
"../browser.ts",
|
|
582
|
+
"../browser.js"
|
|
583
|
+
]);
|
|
584
|
+
return {
|
|
585
|
+
debug: debugShimPath,
|
|
586
|
+
...runtimeEntry ? { "devflare/runtime": runtimeEntry } : {},
|
|
587
|
+
...packageEntry ? { devflare: packageEntry } : {}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
async function bundleWorkerEntry(options) {
|
|
591
|
+
const fs = await import("node:fs/promises");
|
|
592
|
+
const outDir = dirname(options.outFile);
|
|
593
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
594
|
+
await fs.rm(options.outFile, { force: true });
|
|
595
|
+
await fs.rm(`${options.outFile}.map`, { force: true });
|
|
596
|
+
const alias = await resolveInternalAliasMap(outDir);
|
|
597
|
+
const defaults = createWorkerdBundlerDefaults();
|
|
598
|
+
const { inputOptions, outputOptions } = resolveWorkerCompatibleRolldownConfig({
|
|
599
|
+
cwd: options.cwd,
|
|
600
|
+
inputFile: options.inputFile,
|
|
601
|
+
outFile: options.outFile,
|
|
602
|
+
alias,
|
|
603
|
+
...defaults,
|
|
604
|
+
platform: "browser",
|
|
605
|
+
rolldownOptions: options.rolldownOptions,
|
|
606
|
+
sourcemap: options.sourcemap ?? defaults.sourcemap,
|
|
607
|
+
minify: options.minify ?? defaults.minify,
|
|
608
|
+
defaultTsconfigMode: "if-present"
|
|
609
|
+
});
|
|
610
|
+
options.logger?.debug(`Bundling main worker → ${options.outFile}`);
|
|
611
|
+
await writeWorkerCompatibleBundle({
|
|
612
|
+
inputOptions,
|
|
613
|
+
outputOptions,
|
|
614
|
+
outFile: options.outFile
|
|
615
|
+
});
|
|
616
|
+
return options.outFile;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/bundler/do-bundler.ts
|
|
620
|
+
import { resolve as resolve3, dirname as dirname2, relative } from "pathe";
|
|
621
|
+
import picomatch from "picomatch";
|
|
622
|
+
function classToBindingName(className) {
|
|
623
|
+
return className.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toUpperCase();
|
|
624
|
+
}
|
|
625
|
+
async function discoverDOs(cwd, pattern) {
|
|
626
|
+
const discovered = [];
|
|
627
|
+
const files = await discoverDurableObjectFiles(cwd, pattern);
|
|
628
|
+
for (const [filePath, classNames] of files) {
|
|
629
|
+
for (const className of classNames) {
|
|
630
|
+
discovered.push({
|
|
631
|
+
filePath,
|
|
632
|
+
className,
|
|
633
|
+
bindingName: classToBindingName(className)
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return discovered;
|
|
638
|
+
}
|
|
639
|
+
function stripDecoratorSyntax(code) {
|
|
640
|
+
let result = code;
|
|
641
|
+
result = result.replace(/@durableObject\s*\([^)]*\)\s*\n?\s*(?=export\s+class)/g, "");
|
|
642
|
+
result = result.replace(/import\s*\{([^}]*)\bdurableObject\b[^}]*\}\s*from\s*['"]devflare\/runtime['"]\s*;?/g, (match, imports) => {
|
|
643
|
+
const cleanedImports = imports.split(",").map((s) => s.trim()).filter((s) => !s.startsWith("durableObject")).join(", ");
|
|
644
|
+
if (cleanedImports.trim() === "") {
|
|
645
|
+
return "";
|
|
646
|
+
}
|
|
647
|
+
return `import { ${cleanedImports} } from 'devflare/runtime'`;
|
|
648
|
+
});
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
async function bundleDOFile(sourcePath, className, outDir, cwd, bundleOptions) {
|
|
652
|
+
const fs = await import("node:fs/promises");
|
|
653
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
654
|
+
const sourceCode = await fs.readFile(sourcePath, "utf-8");
|
|
655
|
+
const transformedCode = (await transformDurableObject(sourceCode, sourcePath))?.code ?? stripDecoratorSyntax(sourceCode);
|
|
656
|
+
const entryCode = `${transformedCode}
|
|
657
|
+
|
|
658
|
+
// Default export for worker (required by Miniflare)
|
|
659
|
+
export default {
|
|
660
|
+
async fetch(request) {
|
|
661
|
+
return new Response('DO Worker for ${className}', { status: 200 });
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
`;
|
|
665
|
+
const virtualEntryId = resolve3(dirname2(sourcePath), `.devflare-do-${className}.virtual.ts`);
|
|
666
|
+
const classOutDir = resolve3(outDir, className);
|
|
667
|
+
try {
|
|
668
|
+
await fs.rm(classOutDir, { recursive: true, force: true });
|
|
669
|
+
} catch {}
|
|
670
|
+
await fs.mkdir(classOutDir, { recursive: true });
|
|
671
|
+
const debugShimPath = await ensureDebugShim(outDir);
|
|
672
|
+
const virtualEntryPlugin = {
|
|
673
|
+
name: "devflare-do-virtual-entry",
|
|
674
|
+
resolveId(id) {
|
|
675
|
+
if (id === virtualEntryId) {
|
|
676
|
+
return virtualEntryId;
|
|
677
|
+
}
|
|
678
|
+
return null;
|
|
679
|
+
},
|
|
680
|
+
load(id) {
|
|
681
|
+
if (id === virtualEntryId) {
|
|
682
|
+
return entryCode;
|
|
683
|
+
}
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
const userRolldownOptions = bundleOptions?.rolldownOptions;
|
|
688
|
+
const userPlugins = userRolldownOptions?.plugins;
|
|
689
|
+
const mergedPlugins = userPlugins === undefined ? [virtualEntryPlugin] : Array.isArray(userPlugins) ? [virtualEntryPlugin, ...userPlugins] : [virtualEntryPlugin, userPlugins];
|
|
690
|
+
const outFile = resolve3(classOutDir, "index.js");
|
|
691
|
+
const defaults = createWorkerdBundlerDefaults();
|
|
692
|
+
const { inputOptions, outputOptions } = resolveWorkerCompatibleRolldownConfig({
|
|
693
|
+
cwd,
|
|
694
|
+
inputFile: virtualEntryId,
|
|
695
|
+
outFile,
|
|
696
|
+
...defaults,
|
|
697
|
+
platform: "neutral",
|
|
698
|
+
alias: {
|
|
699
|
+
debug: debugShimPath
|
|
700
|
+
},
|
|
701
|
+
rolldownOptions: {
|
|
702
|
+
...userRolldownOptions,
|
|
703
|
+
plugins: mergedPlugins
|
|
704
|
+
},
|
|
705
|
+
sourcemap: bundleOptions?.sourcemap ?? defaults.sourcemap,
|
|
706
|
+
minify: bundleOptions?.minify ?? defaults.minify,
|
|
707
|
+
inlineDynamicImports: true,
|
|
708
|
+
defaultTsconfigMode: "always"
|
|
709
|
+
});
|
|
710
|
+
await writeWorkerCompatibleBundle({
|
|
711
|
+
inputOptions,
|
|
712
|
+
outputOptions,
|
|
713
|
+
outFile
|
|
714
|
+
});
|
|
715
|
+
return resolve3(classOutDir, "index.js");
|
|
716
|
+
}
|
|
717
|
+
async function bundleAllDOs(discovered, outDir, cwd, logger, bundleOptions) {
|
|
718
|
+
const fs = await import("node:fs/promises");
|
|
719
|
+
const bundles = new Map;
|
|
720
|
+
const classes = new Map;
|
|
721
|
+
const sourceFiles = new Map;
|
|
722
|
+
const errors = [];
|
|
723
|
+
for (const do_ of discovered) {
|
|
724
|
+
const existing = sourceFiles.get(do_.filePath) || [];
|
|
725
|
+
existing.push(do_.className);
|
|
726
|
+
sourceFiles.set(do_.filePath, existing);
|
|
727
|
+
}
|
|
728
|
+
for (const do_ of discovered) {
|
|
729
|
+
try {
|
|
730
|
+
logger?.debug(`Bundling ${do_.className} from ${do_.filePath}`);
|
|
731
|
+
const outFile = await bundleDOFile(do_.filePath, do_.className, outDir, cwd, bundleOptions);
|
|
732
|
+
bundles.set(do_.bindingName, outFile);
|
|
733
|
+
classes.set(do_.bindingName, do_.className);
|
|
734
|
+
logger?.debug(` → ${outFile}`);
|
|
735
|
+
} catch (error) {
|
|
736
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
737
|
+
errors.push(err);
|
|
738
|
+
logger?.error(`Failed to bundle ${do_.className}:`, err.message);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return { bundles, classes, sourceFiles, errors };
|
|
742
|
+
}
|
|
743
|
+
function createDOBundler(options) {
|
|
744
|
+
const { cwd, pattern, outDir, logger, onRebuild, rolldownOptions, sourcemap, minify } = options;
|
|
745
|
+
let result = {
|
|
746
|
+
bundles: new Map,
|
|
747
|
+
classes: new Map,
|
|
748
|
+
sourceFiles: new Map,
|
|
749
|
+
errors: []
|
|
750
|
+
};
|
|
751
|
+
let watcher = null;
|
|
752
|
+
let chokidarWatcher = null;
|
|
753
|
+
async function build() {
|
|
754
|
+
const discovered = await discoverDOs(cwd, pattern);
|
|
755
|
+
if (discovered.length === 0) {
|
|
756
|
+
logger?.debug("No DOs found matching pattern:", pattern);
|
|
757
|
+
return result;
|
|
758
|
+
}
|
|
759
|
+
logger?.info(`Found ${discovered.length} Durable Object(s)`);
|
|
760
|
+
for (const do_ of discovered) {
|
|
761
|
+
logger?.info(` • ${do_.className} → ${do_.bindingName}`);
|
|
762
|
+
}
|
|
763
|
+
result = await bundleAllDOs(discovered, outDir, cwd, logger, {
|
|
764
|
+
rolldownOptions,
|
|
765
|
+
sourcemap,
|
|
766
|
+
minify
|
|
767
|
+
});
|
|
768
|
+
if (result.errors.length === 0) {
|
|
769
|
+
logger?.success(`Bundled ${result.bundles.size} DO(s) to ${outDir}`);
|
|
770
|
+
}
|
|
771
|
+
return result;
|
|
772
|
+
}
|
|
773
|
+
async function watch() {
|
|
774
|
+
const chokidar = await import("chokidar");
|
|
775
|
+
const files = await findFiles(pattern, { cwd });
|
|
776
|
+
let dirsToWatch;
|
|
777
|
+
if (files.length > 0) {
|
|
778
|
+
dirsToWatch = [...new Set(files.map((f) => dirname2(f)))];
|
|
779
|
+
} else {
|
|
780
|
+
const patternDir = dirname2(pattern);
|
|
781
|
+
const absolutePatternDir = resolve3(cwd, patternDir === "." ? "" : patternDir) || cwd;
|
|
782
|
+
dirsToWatch = [absolutePatternDir];
|
|
783
|
+
logger?.debug(`No DO files yet, watching pattern directory: ${absolutePatternDir}`);
|
|
784
|
+
}
|
|
785
|
+
logger?.info(`Watching ${files.length} DO file(s) in ${dirsToWatch.length} director(ies)...`);
|
|
786
|
+
const isWindows = process.platform === "win32";
|
|
787
|
+
chokidarWatcher = chokidar.watch(dirsToWatch, {
|
|
788
|
+
ignoreInitial: true,
|
|
789
|
+
usePolling: isWindows,
|
|
790
|
+
interval: isWindows ? 300 : undefined,
|
|
791
|
+
awaitWriteFinish: {
|
|
792
|
+
stabilityThreshold: 100,
|
|
793
|
+
pollInterval: 50
|
|
794
|
+
},
|
|
795
|
+
depth: 0
|
|
796
|
+
});
|
|
797
|
+
const normalizePath = (p) => {
|
|
798
|
+
let normalized = p.replace(/\\/g, "/");
|
|
799
|
+
if (isWindows && /^[a-zA-Z]:/.test(normalized)) {
|
|
800
|
+
normalized = normalized[0].toLowerCase() + normalized.slice(1);
|
|
801
|
+
}
|
|
802
|
+
return normalized;
|
|
803
|
+
};
|
|
804
|
+
const isMatch = picomatch(pattern, {
|
|
805
|
+
dot: true,
|
|
806
|
+
matchBase: false
|
|
807
|
+
});
|
|
808
|
+
const matchesPattern = (filePath) => {
|
|
809
|
+
const normalizedPath = normalizePath(filePath);
|
|
810
|
+
const relativePath = relative(normalizePath(cwd), normalizedPath);
|
|
811
|
+
return isMatch(relativePath);
|
|
812
|
+
};
|
|
813
|
+
let isRebuilding = false;
|
|
814
|
+
let pendingRebuild = null;
|
|
815
|
+
let rebuildTimeout = null;
|
|
816
|
+
const scheduleRebuild = (changedPath) => {
|
|
817
|
+
if (rebuildTimeout) {
|
|
818
|
+
clearTimeout(rebuildTimeout);
|
|
819
|
+
}
|
|
820
|
+
rebuildTimeout = setTimeout(() => {
|
|
821
|
+
triggerRebuild(changedPath);
|
|
822
|
+
}, 150);
|
|
823
|
+
};
|
|
824
|
+
const triggerRebuild = async (changedPath) => {
|
|
825
|
+
if (isRebuilding) {
|
|
826
|
+
pendingRebuild = changedPath;
|
|
827
|
+
logger?.debug(`Rebuild already in progress, queuing: ${changedPath}`);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
isRebuilding = true;
|
|
831
|
+
try {
|
|
832
|
+
logger?.info(`DO file changed: ${changedPath}`);
|
|
833
|
+
logger?.info("Rebuilding DOs...");
|
|
834
|
+
const startTime = Date.now();
|
|
835
|
+
result = await build();
|
|
836
|
+
const elapsed = Date.now() - startTime;
|
|
837
|
+
logger?.success(`DO rebuild complete (${elapsed}ms)`);
|
|
838
|
+
await onRebuild?.(result);
|
|
839
|
+
} catch (error) {
|
|
840
|
+
logger?.error("DO rebuild failed:", error);
|
|
841
|
+
} finally {
|
|
842
|
+
isRebuilding = false;
|
|
843
|
+
if (pendingRebuild) {
|
|
844
|
+
const nextPath = pendingRebuild;
|
|
845
|
+
pendingRebuild = null;
|
|
846
|
+
triggerRebuild(nextPath);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
chokidarWatcher.on("change", (filePath) => {
|
|
851
|
+
if (matchesPattern(filePath)) {
|
|
852
|
+
logger?.debug(`File changed: ${filePath}`);
|
|
853
|
+
scheduleRebuild(filePath);
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
chokidarWatcher.on("add", (filePath) => {
|
|
857
|
+
if (matchesPattern(filePath)) {
|
|
858
|
+
logger?.debug(`File added: ${filePath}`);
|
|
859
|
+
scheduleRebuild(filePath);
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
chokidarWatcher.on("unlink", (filePath) => {
|
|
863
|
+
if (matchesPattern(filePath)) {
|
|
864
|
+
logger?.debug(`File removed: ${filePath}`);
|
|
865
|
+
scheduleRebuild(filePath);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
chokidarWatcher.on("ready", () => {
|
|
869
|
+
logger?.info("DO file watcher ready");
|
|
870
|
+
});
|
|
871
|
+
chokidarWatcher.on("error", (error) => {
|
|
872
|
+
logger?.error("DO file watcher error:", error);
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
async function close() {
|
|
876
|
+
if (watcher) {
|
|
877
|
+
await watcher.close();
|
|
878
|
+
watcher = null;
|
|
879
|
+
}
|
|
880
|
+
if (chokidarWatcher) {
|
|
881
|
+
await chokidarWatcher.close();
|
|
882
|
+
chokidarWatcher = null;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
function getResult() {
|
|
886
|
+
return result;
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
build,
|
|
890
|
+
watch,
|
|
891
|
+
close,
|
|
892
|
+
getResult
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
export { createDOBundler, bundleWorkerEntry };
|