@sjcx/vorma 0.0.0-pre.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/LICENSE +20 -0
- package/dist/build/assemble-artifacts.d.ts +23 -0
- package/dist/build/browser-contracts.d.ts +8 -0
- package/dist/build/bundle-client.d.ts +29 -0
- package/dist/build/cli.d.ts +2 -0
- package/dist/build/cli.js +1256 -0
- package/dist/build/client-inputs.d.ts +11 -0
- package/dist/build/client-output-hashing.d.ts +3 -0
- package/dist/build/client-public-urls.d.ts +20 -0
- package/dist/build/compare-text.d.ts +5 -0
- package/dist/build/compile-application.d.ts +40 -0
- package/dist/build/compile-server.d.ts +42 -0
- package/dist/build/contained-path.d.ts +29 -0
- package/dist/build/critical-css.d.ts +7 -0
- package/dist/build/css-public-assets.d.ts +15 -0
- package/dist/build/development-artifacts.d.ts +35 -0
- package/dist/build/development-browser-source.d.ts +3 -0
- package/dist/build/development-build.d.ts +30 -0
- package/dist/build/development-changes.d.ts +14 -0
- package/dist/build/development-client.d.ts +21 -0
- package/dist/build/development-generation.d.ts +20 -0
- package/dist/build/development-listener.d.ts +14 -0
- package/dist/build/development-patterns.d.ts +5 -0
- package/dist/build/development-protocol.d.ts +20 -0
- package/dist/build/development-server-process.d.ts +30 -0
- package/dist/build/development-sources.d.ts +23 -0
- package/dist/build/development-stylesheet.d.ts +15 -0
- package/dist/build/development-watcher.d.ts +15 -0
- package/dist/build/development-work.d.ts +32 -0
- package/dist/build/development.d.ts +3 -0
- package/dist/build/generate-application.d.ts +62 -0
- package/dist/build/index.d.ts +15 -0
- package/dist/build/index.js +2 -0
- package/dist/build/inspect-css-artifacts.d.ts +11 -0
- package/dist/build/inspect-projects.d.ts +69 -0
- package/dist/build/load-config.d.ts +56 -0
- package/dist/build/output-files.d.ts +8 -0
- package/dist/build/output-ownership.d.ts +21 -0
- package/dist/build/public-assets.d.ts +32 -0
- package/dist/build/publish-artifacts.d.ts +23 -0
- package/dist/build/render-client-contracts.d.ts +10 -0
- package/dist/build/render-client-transport.d.ts +11 -0
- package/dist/build/render-server-generated.d.ts +33 -0
- package/dist/build/render-source-text.d.ts +24 -0
- package/dist/build/render-types.d.ts +31 -0
- package/dist/build/resolve-config.d.ts +50 -0
- package/dist/build/source-layout.d.ts +24 -0
- package/dist/build/typescript-projects.d.ts +36 -0
- package/dist/build-JgARonaY.js +3214 -0
- package/dist/build-invocation-D7Fu8gBK.js +4 -0
- package/dist/client/assembly.d.ts +40 -0
- package/dist/client/browser-http-client.d.ts +10 -0
- package/dist/client/client-loaders.d.ts +93 -0
- package/dist/client/commit.d.ts +41 -0
- package/dist/client/contracts.d.ts +192 -0
- package/dist/client/core.d.ts +45 -0
- package/dist/client/development.d.ts +16 -0
- package/dist/client/document-recovery.d.ts +4 -0
- package/dist/client/endpoints.d.ts +54 -0
- package/dist/client/errors.d.ts +87 -0
- package/dist/client/freshness.d.ts +51 -0
- package/dist/client/head.d.ts +14 -0
- package/dist/client/history.d.ts +25 -0
- package/dist/client/index.d.ts +80 -0
- package/dist/client/index.js +150 -0
- package/dist/client/links.d.ts +48 -0
- package/dist/client/materialize.d.ts +21 -0
- package/dist/client/modules.d.ts +2 -0
- package/dist/client/navigation.d.ts +47 -0
- package/dist/client/payload.d.ts +54 -0
- package/dist/client/prefetch.d.ts +28 -0
- package/dist/client/prepare.d.ts +47 -0
- package/dist/client/projections.d.ts +26 -0
- package/dist/client/queries.d.ts +33 -0
- package/dist/client/react/application.d.ts +87 -0
- package/dist/client/react/hooks.d.ts +36 -0
- package/dist/client/react/index.d.ts +5 -0
- package/dist/client/react/index.js +3237 -0
- package/dist/client/react/link.d.ts +27 -0
- package/dist/client/react/view.d.ts +86 -0
- package/dist/client/request.d.ts +16 -0
- package/dist/client/route-context.d.ts +31 -0
- package/dist/client/route-matching.d.ts +22 -0
- package/dist/client/route-synchronization.d.ts +30 -0
- package/dist/client/route-url.d.ts +5 -0
- package/dist/client/runtime.d.ts +82 -0
- package/dist/client/scroll.d.ts +16 -0
- package/dist/client/services.d.ts +34 -0
- package/dist/client/state.d.ts +102 -0
- package/dist/client/static-assets.d.ts +3 -0
- package/dist/client/stylesheets.d.ts +7 -0
- package/dist/client/transport-type-entries.d.ts +2 -0
- package/dist/client/types.d.ts +152 -0
- package/dist/client/view-definition.d.ts +24 -0
- package/dist/client/work-indicator.d.ts +18 -0
- package/dist/compile-Me8EOyMK.js +870 -0
- package/dist/development-B_rxuwY7.js +128 -0
- package/dist/hash-set-BPx3iXec.js +141 -0
- package/dist/href-Cqyax8Y0.js +357 -0
- package/dist/materialize-DZ7VSLFk.js +739 -0
- package/dist/metadata-BoPKo-G2.js +32 -0
- package/dist/primitives/tasks/index.d.ts +1 -0
- package/dist/primitives/tasks/index.js +1 -0
- package/dist/route-context-CtY_9U_W.js +559 -0
- package/dist/route-matching-D_uSG2P9.js +33 -0
- package/dist/search-parse-0f7oqVsG.js +375 -0
- package/dist/server/config/index.d.ts +36 -0
- package/dist/server/config/index.js +30 -0
- package/dist/server/development/metadata.d.ts +11 -0
- package/dist/server/document.d.ts +37 -0
- package/dist/server/form-data.d.ts +21 -0
- package/dist/server/graph/compile.d.ts +118 -0
- package/dist/server/graph/declarations.d.ts +117 -0
- package/dist/server/graph/literal-error-tag.d.ts +14 -0
- package/dist/server/graph/pattern-conflicts.d.ts +36 -0
- package/dist/server/graph/search-schema.d.ts +13 -0
- package/dist/server/graph/transport-schema.d.ts +11 -0
- package/dist/server/index.d.ts +310 -0
- package/dist/server/index.js +2311 -0
- package/dist/server/public-assets.d.ts +18 -0
- package/dist/server/runtime/application.d.ts +15 -0
- package/dist/server/runtime/assets.d.ts +15 -0
- package/dist/server/runtime/context.d.ts +96 -0
- package/dist/server/runtime/domain-errors.d.ts +11 -0
- package/dist/server/runtime/effects.d.ts +83 -0
- package/dist/server/runtime/execute.d.ts +13 -0
- package/dist/server/runtime/finalize.d.ts +44 -0
- package/dist/server/runtime/handler.d.ts +106 -0
- package/dist/server/runtime/head.d.ts +22 -0
- package/dist/server/runtime/payload.d.ts +50 -0
- package/dist/server/runtime/pipeline.d.ts +27 -0
- package/dist/server/runtime/plan.d.ts +98 -0
- package/dist/server/runtime/render-document.d.ts +16 -0
- package/dist/server/runtime/startup-error.d.ts +11 -0
- package/dist/server/runtime/transport-type-entries.d.ts +2 -0
- package/dist/server/runtime/vercel-deployment.d.ts +2 -0
- package/dist/server/url-pathname.d.ts +13 -0
- package/dist/static-assets-BrH3O0M8.js +199 -0
- package/dist/universal/content-digest.d.ts +7 -0
- package/dist/universal/generated-types.d.ts +9 -0
- package/dist/universal/href.d.ts +17 -0
- package/dist/universal/http-method.d.ts +4 -0
- package/dist/universal/json-transport.d.ts +14 -0
- package/dist/universal/null-prototype-record.d.ts +7 -0
- package/dist/universal/path-wire.d.ts +71 -0
- package/dist/universal/pattern-language.d.ts +4 -0
- package/dist/universal/protocol-schemas.d.ts +99 -0
- package/dist/universal/protocol.d.ts +162 -0
- package/dist/universal/search-grammar.d.ts +7 -0
- package/dist/universal/search-parse.d.ts +22 -0
- package/dist/universal/search-serialize.d.ts +16 -0
- package/dist/universal/search-shape.d.ts +32 -0
- package/dist/universal/static-assets.d.ts +2 -0
- package/dist/universal/transport-types/big-decimal.d.ts +13 -0
- package/dist/universal/transport-types/chunk.d.ts +8 -0
- package/dist/universal/transport-types/date-time-utc.d.ts +13 -0
- package/dist/universal/transport-types/date-time-zoned.d.ts +13 -0
- package/dist/universal/transport-types/duration.d.ts +13 -0
- package/dist/universal/transport-types/hash-map.d.ts +8 -0
- package/dist/universal/transport-types/hash-set.d.ts +8 -0
- package/dist/universal/transport-types/option.d.ts +10 -0
- package/dist/universal/transport-types/redacted.d.ts +9 -0
- package/dist/universal/value-transport.d.ts +117 -0
- package/dist/value-transport-Bst4Hk-n.js +864 -0
- package/dist/vite/build-invocation.d.ts +4 -0
- package/dist/vite/index.d.ts +3 -0
- package/dist/vite/index.js +15 -0
- package/package.json +84 -0
|
@@ -0,0 +1,1256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { C as toContentDigest, S as encodeCanonicalJson, _ as compileServerApplication, a as acquireOutputOwnership, b as getClientViewEntries, c as writeAndCheckApplicationGeneration, d as withTypescriptProjects, f as inspectProjects, g as createClientPublicUrlPlugin, h as createCssPublicAssets, i as publishApplicationArtifacts, l as collectPublicAssets, m as resolveVormaConfig, n as loadApplicationSourceLayout, o as checkOutputPath, p as getGeneratedSourceDirectories, r as preparePublicOutput, s as assembleApplicationGeneration, t as buildApplication, u as readTypescriptProjectRoots, v as containsRelativePath, x as projectBrowserContracts, y as CLIENT_SHARED_PACKAGES } from "../build-JgARonaY.js";
|
|
3
|
+
import { g as DEVELOPMENT_PROTOCOL, y as FRAMEWORK_IDENTITY_PROTOCOL } from "../value-transport-Bst4Hk-n.js";
|
|
4
|
+
import { t as VORMA_BUILD_PLUGIN_NAME } from "../build-invocation-D7Fu8gBK.js";
|
|
5
|
+
import { VormaConfigSchema } from "../server/config/index.js";
|
|
6
|
+
import { NodeChildProcessSpawner, NodeCrypto, NodeFileSystem, NodePath, NodeRuntime, NodeSocketServer, NodeStdio, NodeTerminal } from "@effect/platform-node-shared";
|
|
7
|
+
import { Cause, Console, Deferred, Effect, Exit, Fiber, FileSystem, Layer, Path, Queue, Result, Schedule, Schema, ScopedRef, Stream } from "effect";
|
|
8
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
9
|
+
import { createFilter, createServer, loadConfigFromFile, normalizePath } from "vite";
|
|
10
|
+
import { dirname, isAbsolute, posix, relative, sep } from "node:path";
|
|
11
|
+
import { RolldownMagicString } from "rolldown";
|
|
12
|
+
import { createServer as createServer$1 } from "node:http";
|
|
13
|
+
import { FetchHttpClient, HttpClient } from "effect/unstable/http";
|
|
14
|
+
import { ChildProcess } from "effect/unstable/process";
|
|
15
|
+
import { watch } from "chokidar";
|
|
16
|
+
//#region package.json
|
|
17
|
+
var version = "0.87.0-pre.0";
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/build/development-artifacts.ts
|
|
20
|
+
function toDevelopmentModuleUrl(input) {
|
|
21
|
+
const pathname = input.id.startsWith("\0") ? `/@id/__x00__${input.id.slice(1)}` : `/@fs/${normalizePath(input.id).replace(/^\//, "")}`;
|
|
22
|
+
return input.origin + pathname.split("/").map(encodeURIComponent).join("/").replaceAll("%40", "@").replaceAll("%3A", ":");
|
|
23
|
+
}
|
|
24
|
+
/** Keeps source URLs and browser contracts on the server; responses project only requested views. */
|
|
25
|
+
function assembleDevelopmentArtifacts(input) {
|
|
26
|
+
const generation = input.build.generation.application;
|
|
27
|
+
function toModule(id) {
|
|
28
|
+
return {
|
|
29
|
+
importUrl: toDevelopmentModuleUrl({
|
|
30
|
+
origin: input.client.origin,
|
|
31
|
+
id
|
|
32
|
+
}),
|
|
33
|
+
depUrls: [],
|
|
34
|
+
cssBundleUrls: [],
|
|
35
|
+
staticAssetUrls: []
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const clientViews = Object.fromEntries(getClientViewEntries(generation).map((view) => {
|
|
39
|
+
return [view.pattern, {
|
|
40
|
+
...toModule(view.id),
|
|
41
|
+
exportNames: view.exportNames
|
|
42
|
+
}];
|
|
43
|
+
}));
|
|
44
|
+
return { metadata: {
|
|
45
|
+
runtimeMode: "development",
|
|
46
|
+
clientBuildId: toContentDigest(encodeCanonicalJson({
|
|
47
|
+
contracts: projectBrowserContracts(generation.inspection),
|
|
48
|
+
clientTransport: generation.clientTransport.source,
|
|
49
|
+
views: clientViews,
|
|
50
|
+
publicFilemap: generation.publicAssets.publicFilemap
|
|
51
|
+
})).slice(0, 24),
|
|
52
|
+
clientEntry: toModule(generation.config.clientEntryFilePath),
|
|
53
|
+
clientViews,
|
|
54
|
+
criticalCss: input.build.stylesheet.css,
|
|
55
|
+
developmentHeadHtml: input.developmentHeadHtml,
|
|
56
|
+
publicFilemap: generation.publicAssets.publicFilemap,
|
|
57
|
+
publicFiles: generation.publicAssets.assets.map((asset) => {
|
|
58
|
+
return {
|
|
59
|
+
url: asset.url,
|
|
60
|
+
fileName: asset.fileName,
|
|
61
|
+
immutable: true
|
|
62
|
+
};
|
|
63
|
+
})
|
|
64
|
+
} };
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/build/development-protocol.ts
|
|
68
|
+
function toDevelopmentClientInputs(generation, sources) {
|
|
69
|
+
return {
|
|
70
|
+
viteConfigFilePath: generation.config.viteConfigFilePath,
|
|
71
|
+
clientEntryFilePath: generation.config.clientEntryFilePath,
|
|
72
|
+
clientViewModules: generation.model.clientViewModules,
|
|
73
|
+
coordinatedResolutionFiles: sources.resolutionFiles,
|
|
74
|
+
generatedDirectories: [generation.config.serverGeneratedDirectory, generation.config.clientGeneratedDirectory],
|
|
75
|
+
clientTransport: generation.clientTransport,
|
|
76
|
+
views: getClientViewEntries(generation),
|
|
77
|
+
publicUrlSites: generation.inspection.publicUrlSites,
|
|
78
|
+
clientSourceTexts: Object.fromEntries(generation.inspection.clientSourceTexts),
|
|
79
|
+
publicAssets: generation.publicAssets.assets,
|
|
80
|
+
publicFilemap: generation.publicAssets.publicFilemap,
|
|
81
|
+
publicDirectory: generation.config.publicDirectory
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/build/development-stylesheet.ts
|
|
86
|
+
/** Reads CSS and its dependency files from Vite's native direct-CSS pipeline. */
|
|
87
|
+
function readDevelopmentStylesheet(input) {
|
|
88
|
+
return Effect.gen(function* () {
|
|
89
|
+
const environment = input.server.environments.client;
|
|
90
|
+
if (environment === void 0) return yield* Effect.fail({
|
|
91
|
+
_tag: "DevelopmentStylesheetIssue",
|
|
92
|
+
filePath: input.filePath,
|
|
93
|
+
cause: "Vite has no client environment"
|
|
94
|
+
});
|
|
95
|
+
const url = `/@fs/${normalizePath(input.filePath)}?direct`;
|
|
96
|
+
const transformed = yield* Effect.tryPromise({
|
|
97
|
+
try: async () => {
|
|
98
|
+
const previous = await environment.moduleGraph.getModuleByUrl(url);
|
|
99
|
+
if (previous !== void 0) environment.moduleGraph.invalidateModule(previous);
|
|
100
|
+
return {
|
|
101
|
+
result: await environment.transformRequest(url),
|
|
102
|
+
module: await environment.moduleGraph.getModuleByUrl(url)
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
catch: (cause) => {
|
|
106
|
+
return {
|
|
107
|
+
_tag: "DevelopmentStylesheetIssue",
|
|
108
|
+
filePath: input.filePath,
|
|
109
|
+
cause
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
if (transformed.result === null || transformed.module === void 0) return yield* Effect.fail({
|
|
114
|
+
_tag: "DevelopmentStylesheetIssue",
|
|
115
|
+
filePath: input.filePath,
|
|
116
|
+
cause: "Vite did not produce the requested stylesheet"
|
|
117
|
+
});
|
|
118
|
+
const visited = /* @__PURE__ */ new Set();
|
|
119
|
+
const pending = [transformed.module];
|
|
120
|
+
const files = /* @__PURE__ */ new Set();
|
|
121
|
+
while (pending.length > 0) {
|
|
122
|
+
const module = pending.pop();
|
|
123
|
+
if (module === void 0 || visited.has(module)) continue;
|
|
124
|
+
visited.add(module);
|
|
125
|
+
if (module.file !== null) files.add(module.file);
|
|
126
|
+
pending.push(...module.importedModules);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
css: transformed.result.code,
|
|
130
|
+
inputFilePaths: [...files]
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/build/development-build.ts
|
|
136
|
+
/** Prepares server bytes and native CSS concurrently after their generated inputs exist. */
|
|
137
|
+
function prepareDevelopmentBuild(input) {
|
|
138
|
+
return Effect.gen(function* () {
|
|
139
|
+
const { generation, client, previous } = input;
|
|
140
|
+
const { application } = generation;
|
|
141
|
+
const inputsChanged = previous === void 0 || previous.work.inspectSources || previous.work.capturePublicAssets;
|
|
142
|
+
return {
|
|
143
|
+
generation,
|
|
144
|
+
...yield* Effect.all({
|
|
145
|
+
server: previous === void 0 || previous.work.compileServer ? compileServerApplication({
|
|
146
|
+
generation: application,
|
|
147
|
+
sourcemap: "inline"
|
|
148
|
+
}) : Effect.succeed(previous.build.server),
|
|
149
|
+
stylesheet: Effect.gen(function* () {
|
|
150
|
+
if (previous !== void 0 && inputsChanged) client.updateInputsFn(toDevelopmentClientInputs(application, generation.sources));
|
|
151
|
+
if (previous !== void 0 && !previous.work.prepareCriticalCss) return previous.build.stylesheet;
|
|
152
|
+
const filePath = application.config.criticalCssEntryFilePath;
|
|
153
|
+
return filePath === void 0 ? {
|
|
154
|
+
css: "",
|
|
155
|
+
inputFilePaths: []
|
|
156
|
+
} : yield* readDevelopmentStylesheet({
|
|
157
|
+
server: client.server,
|
|
158
|
+
filePath
|
|
159
|
+
});
|
|
160
|
+
})
|
|
161
|
+
}, { concurrency: 2 })
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/** Browser-only development controls use Vite's authenticated HMR connection. */
|
|
166
|
+
function renderDevelopmentBrowserSource() {
|
|
167
|
+
return String.raw`
|
|
168
|
+
let rebuilding;
|
|
169
|
+
let buildState = "ready";
|
|
170
|
+
let revalidations = 0;
|
|
171
|
+
function showRebuilding(failed) {
|
|
172
|
+
if (!rebuilding) {
|
|
173
|
+
rebuilding = document.createElement("div");
|
|
174
|
+
rebuilding.id = "vorma-rebuilding-overlay";
|
|
175
|
+
rebuilding.style.cssText = "position:fixed;bottom:16px;right:16px;z-index:2147483647;color:white;padding:10px 16px;border-radius:4px;font:14px system-ui";
|
|
176
|
+
document.body.append(rebuilding);
|
|
177
|
+
}
|
|
178
|
+
rebuilding.textContent = failed ? "Build error. See the console for details." : "Rebuilding…";
|
|
179
|
+
rebuilding.setAttribute("role", failed ? "alert" : "status");
|
|
180
|
+
rebuilding.style.backgroundColor = failed ? "#700" : "#222";
|
|
181
|
+
}
|
|
182
|
+
function hideRebuilding() {
|
|
183
|
+
rebuilding?.remove();
|
|
184
|
+
rebuilding = undefined;
|
|
185
|
+
}
|
|
186
|
+
function updateRebuilding() {
|
|
187
|
+
if (buildState === "error") showRebuilding(true);
|
|
188
|
+
else if (buildState === "rebuilding" || revalidations > 0) showRebuilding(false);
|
|
189
|
+
else hideRebuilding();
|
|
190
|
+
}
|
|
191
|
+
if (import.meta.hot) {
|
|
192
|
+
import.meta.hot.on("vorma:development", (message) => {
|
|
193
|
+
if (message.kind === "rebuilding") {
|
|
194
|
+
buildState = "rebuilding";
|
|
195
|
+
updateRebuilding();
|
|
196
|
+
} else if (message.kind === "error") {
|
|
197
|
+
console.error("[vorma] Build error", message.message);
|
|
198
|
+
buildState = "error";
|
|
199
|
+
updateRebuilding();
|
|
200
|
+
} else if (message.kind === "ready") {
|
|
201
|
+
buildState = "ready";
|
|
202
|
+
updateRebuilding();
|
|
203
|
+
} else if (message.kind === "css") {
|
|
204
|
+
let style = document.getElementById("vorma-critical-css");
|
|
205
|
+
if (!style) {
|
|
206
|
+
style = document.createElement("style");
|
|
207
|
+
style.id = "vorma-critical-css";
|
|
208
|
+
document.head.append(style);
|
|
209
|
+
}
|
|
210
|
+
style.textContent = message.css;
|
|
211
|
+
} else if (message.kind === "revalidate") {
|
|
212
|
+
buildState = "ready";
|
|
213
|
+
revalidations++;
|
|
214
|
+
updateRebuilding();
|
|
215
|
+
// Vite awaits event handlers; a data request must not block later CSS or build messages.
|
|
216
|
+
Promise.resolve().then(() => window.__vorma_development?.revalidateFn()).then(
|
|
217
|
+
() => {},
|
|
218
|
+
(cause) => console.error("[vorma] Data revalidation failed", cause),
|
|
219
|
+
).then(() => {
|
|
220
|
+
revalidations--;
|
|
221
|
+
updateRebuilding();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
import.meta.hot.dispose(hideRebuilding);
|
|
226
|
+
}
|
|
227
|
+
`;
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/build/development-listener.ts
|
|
231
|
+
/** Hosts only Vite middleware and its WebSocket; the application has its own server. */
|
|
232
|
+
function createDevelopmentListener(port) {
|
|
233
|
+
return Effect.gen(function* () {
|
|
234
|
+
function refuseRequestFn(...[, response]) {
|
|
235
|
+
response.writeHead(503).end();
|
|
236
|
+
}
|
|
237
|
+
let handleRequestFn = refuseRequestFn;
|
|
238
|
+
const server = yield* Effect.acquireRelease(Effect.sync(() => {
|
|
239
|
+
return createServer$1((request, response) => handleRequestFn(request, response));
|
|
240
|
+
}), (server) => {
|
|
241
|
+
return Effect.callback((resume) => {
|
|
242
|
+
server.close(() => resume(Effect.void));
|
|
243
|
+
server.closeAllConnections();
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
server,
|
|
248
|
+
listen: Effect.callback((resume) => {
|
|
249
|
+
function failFn(cause) {
|
|
250
|
+
resume(Effect.fail({
|
|
251
|
+
_tag: "DevelopmentListenerIssue",
|
|
252
|
+
cause
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
server.once("error", failFn);
|
|
256
|
+
server.listen(port, "127.0.0.1", () => {
|
|
257
|
+
server.off("error", failFn);
|
|
258
|
+
resume(Effect.void);
|
|
259
|
+
});
|
|
260
|
+
}),
|
|
261
|
+
port,
|
|
262
|
+
origin: `http://127.0.0.1:${port}`,
|
|
263
|
+
setHandlerFn(next) {
|
|
264
|
+
handleRequestFn = next;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/build/development-client.ts
|
|
271
|
+
/** Starts Vite for one inspected application; the surrounding Effect scope closes it. */
|
|
272
|
+
function startDevelopmentClient(inputs, port, recordChangeFn) {
|
|
273
|
+
return Effect.gen(function* () {
|
|
274
|
+
const listener = yield* createDevelopmentListener(port);
|
|
275
|
+
let activeServer;
|
|
276
|
+
let nativeRestart;
|
|
277
|
+
let configurationRevision = 0;
|
|
278
|
+
let coordinatedRestart = false;
|
|
279
|
+
let views = inputs.views;
|
|
280
|
+
const virtualSources = new Map(views.flatMap((view) => {
|
|
281
|
+
return view.source === void 0 ? [] : [[view.id, view.source]];
|
|
282
|
+
}));
|
|
283
|
+
let declared = false;
|
|
284
|
+
let inspection;
|
|
285
|
+
let finishInspectionFn;
|
|
286
|
+
function pauseInspectionFn(filePath) {
|
|
287
|
+
if (activeServer?.config.configFileDependencies.includes(normalizePath(filePath))) return;
|
|
288
|
+
if (inputs.clientSourceTexts[normalizePath(filePath)] !== void 0 && inspection === void 0) inspection = new Promise((resolve) => {
|
|
289
|
+
finishInspectionFn = resolve;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
function resumeInspectionFn(reload) {
|
|
293
|
+
finishInspectionFn?.(reload);
|
|
294
|
+
inspection = void 0;
|
|
295
|
+
finishInspectionFn = void 0;
|
|
296
|
+
}
|
|
297
|
+
const integration = {
|
|
298
|
+
name: VORMA_BUILD_PLUGIN_NAME,
|
|
299
|
+
enforce: "pre",
|
|
300
|
+
api: { registerDeclarationFn: () => {
|
|
301
|
+
declared = true;
|
|
302
|
+
} },
|
|
303
|
+
config() {
|
|
304
|
+
declared = false;
|
|
305
|
+
return {
|
|
306
|
+
base: "/",
|
|
307
|
+
publicDir: false,
|
|
308
|
+
appType: "custom",
|
|
309
|
+
resolve: { dedupe: CLIENT_SHARED_PACKAGES },
|
|
310
|
+
oxc: {
|
|
311
|
+
jsxRefreshInclude: /\.[cm]?[jt]sx?(?:$|\?)/,
|
|
312
|
+
jsxRefreshExclude: [/\/node_modules\//, ...inputs.clientViewModules.map((file) => {
|
|
313
|
+
return new RegExp(`^${normalizePath(file).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\?.*)?$`);
|
|
314
|
+
})]
|
|
315
|
+
},
|
|
316
|
+
optimizeDeps: { entries: [inputs.clientEntryFilePath, ...inputs.clientViewModules] },
|
|
317
|
+
server: {
|
|
318
|
+
middlewareMode: true,
|
|
319
|
+
origin: listener.origin,
|
|
320
|
+
hmr: {
|
|
321
|
+
server: listener.server,
|
|
322
|
+
clientPort: listener.port
|
|
323
|
+
},
|
|
324
|
+
headers: { "cache-control": "no-store" },
|
|
325
|
+
watch: { ignored: (filePath) => {
|
|
326
|
+
const file = normalizePath(filePath);
|
|
327
|
+
return inputs.coordinatedResolutionFiles.has(file) || inputs.generatedDirectories.some((directory) => {
|
|
328
|
+
return file === normalizePath(directory) || file.startsWith(`${normalizePath(directory)}/`);
|
|
329
|
+
});
|
|
330
|
+
} }
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
configureServer(server) {
|
|
335
|
+
server.watcher.on("change", pauseInspectionFn);
|
|
336
|
+
server.watcher.on("unlink", pauseInspectionFn);
|
|
337
|
+
if (activeServer !== void 0 && !coordinatedRestart) {
|
|
338
|
+
nativeRestart = activeServer.restart();
|
|
339
|
+
nativeRestart.then(() => {
|
|
340
|
+
configurationRevision++;
|
|
341
|
+
recordChangeFn(inputs.viteConfigFilePath);
|
|
342
|
+
}, (cause) => {
|
|
343
|
+
server.config.logger.error(String(cause));
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
configResolved: {
|
|
348
|
+
order: "post",
|
|
349
|
+
handler() {
|
|
350
|
+
if (!declared) this.error("Vorma development requires vorma() from vorma/vite in the configured Vite plugins list.");
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
resolveId(source) {
|
|
354
|
+
if (source === "\0vorma:development-browser") return source;
|
|
355
|
+
if (virtualSources.has(source)) return source;
|
|
356
|
+
return source === FRAMEWORK_IDENTITY_PROTOCOL.moduleSpecifiers.clientTransportTypes ? inputs.clientTransport.filePath : null;
|
|
357
|
+
},
|
|
358
|
+
async load(id) {
|
|
359
|
+
if (id === "\0vorma:development-browser") return renderDevelopmentBrowserSource();
|
|
360
|
+
if (inputs.clientSourceTexts[id.split("?", 1)[0] ?? id] !== void 0) await inspection;
|
|
361
|
+
return virtualSources.get(id) ?? null;
|
|
362
|
+
},
|
|
363
|
+
async hotUpdate() {
|
|
364
|
+
if (await inspection) return [];
|
|
365
|
+
},
|
|
366
|
+
transform(code, id) {
|
|
367
|
+
if (!views.some((view) => view.id === id)) return null;
|
|
368
|
+
const source = new RolldownMagicString(code);
|
|
369
|
+
source.append("\nif (import.meta.hot) { import.meta.hot.accept((module) => { if (module) window.__vorma_development?.updateModuleFn(import.meta.url, module); }); }");
|
|
370
|
+
return {
|
|
371
|
+
code: source.toString(),
|
|
372
|
+
map: source.generateMap({ hires: true }).toString()
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
const publicCss = createCssPublicAssets(inputs);
|
|
377
|
+
function getPublicUrlInput(application) {
|
|
378
|
+
return {
|
|
379
|
+
sites: application.publicUrlSites,
|
|
380
|
+
sourceTexts: new Map(Object.entries(application.clientSourceTexts)),
|
|
381
|
+
publicAssets: application.publicAssets,
|
|
382
|
+
publicDirectory: application.publicDirectory,
|
|
383
|
+
referencedAssets: /* @__PURE__ */ new Map()
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
const publicUrls = createClientPublicUrlPlugin(getPublicUrlInput(inputs));
|
|
387
|
+
const server = yield* Effect.acquireRelease(Effect.tryPromise({
|
|
388
|
+
try: () => {
|
|
389
|
+
return createServer({
|
|
390
|
+
configFile: inputs.viteConfigFilePath,
|
|
391
|
+
plugins: [
|
|
392
|
+
integration,
|
|
393
|
+
publicCss.plugin,
|
|
394
|
+
publicUrls
|
|
395
|
+
]
|
|
396
|
+
});
|
|
397
|
+
},
|
|
398
|
+
catch: (cause) => {
|
|
399
|
+
return {
|
|
400
|
+
_tag: "DevelopmentClientIssue",
|
|
401
|
+
operation: "create",
|
|
402
|
+
cause
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}), (server) => {
|
|
406
|
+
return Effect.tryPromise({
|
|
407
|
+
try: async () => {
|
|
408
|
+
await nativeRestart;
|
|
409
|
+
return Promise.all(Object.values(server.environments).map((environment) => {
|
|
410
|
+
return environment.waitForRequestsIdle();
|
|
411
|
+
}));
|
|
412
|
+
},
|
|
413
|
+
catch: (cause) => {
|
|
414
|
+
return {
|
|
415
|
+
_tag: "DevelopmentClientIssue",
|
|
416
|
+
operation: "close",
|
|
417
|
+
cause
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
}).pipe(Effect.ensuring(Effect.tryPromise(() => server.close()).pipe(Effect.catch((issue) => {
|
|
421
|
+
return Effect.logError("Could not close Vite", issue);
|
|
422
|
+
}))), Effect.catch((issue) => {
|
|
423
|
+
return Effect.logError("Could not close Vite development resources", issue);
|
|
424
|
+
}));
|
|
425
|
+
});
|
|
426
|
+
activeServer = server;
|
|
427
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => resumeInspectionFn(true)));
|
|
428
|
+
listener.setHandlerFn((request, response) => {
|
|
429
|
+
server.middlewares(request, response, () => response.writeHead(404).end());
|
|
430
|
+
});
|
|
431
|
+
return {
|
|
432
|
+
server,
|
|
433
|
+
get configurationRevision() {
|
|
434
|
+
return configurationRevision;
|
|
435
|
+
},
|
|
436
|
+
restart: Effect.suspend(() => {
|
|
437
|
+
coordinatedRestart = true;
|
|
438
|
+
return Effect.tryPromise(() => server.restart()).pipe(Effect.ensuring(Effect.sync(() => {
|
|
439
|
+
coordinatedRestart = false;
|
|
440
|
+
})));
|
|
441
|
+
}).pipe(Effect.uninterruptible),
|
|
442
|
+
origin: listener.origin,
|
|
443
|
+
listen: listener.listen,
|
|
444
|
+
get views() {
|
|
445
|
+
return views;
|
|
446
|
+
},
|
|
447
|
+
resumeInspectionFn,
|
|
448
|
+
readHeadHtml: Effect.tryPromise({
|
|
449
|
+
try: async () => {
|
|
450
|
+
const html = await server.transformIndexHtml("/", "<head></head>");
|
|
451
|
+
return html.slice(html.indexOf("<head>") + 6, html.lastIndexOf("</head>")).replaceAll("\"/@vite/client\"", JSON.stringify(`${listener.origin}/@vite/client`)).replaceAll("\"/@react-refresh\"", JSON.stringify(`${listener.origin}/@react-refresh`)) + `<script type="module" src="${listener.origin}/@id/__x00__vorma:development-browser"><\/script>`;
|
|
452
|
+
},
|
|
453
|
+
catch: (cause) => {
|
|
454
|
+
return {
|
|
455
|
+
_tag: "DevelopmentClientIssue",
|
|
456
|
+
operation: "create",
|
|
457
|
+
cause
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}),
|
|
461
|
+
/** Replaces inspected sources and asset references within the existing Vite configuration. */
|
|
462
|
+
updateInputsFn(next) {
|
|
463
|
+
const replacedFiles = /* @__PURE__ */ new Set([...Object.keys(inputs.clientSourceTexts), ...Object.keys(next.clientSourceTexts)]);
|
|
464
|
+
if (inputs.clientTransport.source !== next.clientTransport.source) replacedFiles.add(next.clientTransport.filePath);
|
|
465
|
+
const publicAssetsChanged = JSON.stringify(inputs.publicFilemap) !== JSON.stringify(next.publicFilemap);
|
|
466
|
+
inputs = next;
|
|
467
|
+
views = next.views;
|
|
468
|
+
const previousVirtualIds = new Set(virtualSources.keys());
|
|
469
|
+
virtualSources.clear();
|
|
470
|
+
for (const view of views) if (view.source !== void 0) virtualSources.set(view.id, view.source);
|
|
471
|
+
publicUrls.api.updateInputFn(getPublicUrlInput(next));
|
|
472
|
+
publicCss.updatePublicAssetsFn(next);
|
|
473
|
+
for (const environment of Object.values(server.environments)) {
|
|
474
|
+
const graph = environment.moduleGraph;
|
|
475
|
+
for (const filePath of replacedFiles) for (const module of graph.getModulesByFile(filePath) ?? []) graph.invalidateModule(module);
|
|
476
|
+
for (const id of previousVirtualIds) {
|
|
477
|
+
const module = graph.getModuleById(id);
|
|
478
|
+
if (module !== void 0) graph.invalidateModule(module);
|
|
479
|
+
}
|
|
480
|
+
if (publicAssetsChanged) {
|
|
481
|
+
for (const module of graph.idToModuleMap.values()) if (module.type === "css") graph.invalidateModule(module);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region src/build/development-sources.ts
|
|
490
|
+
/** Inspects declarations and records native compiler inputs without replacing filesystem behavior. */
|
|
491
|
+
function inspectDevelopmentSources(layout) {
|
|
492
|
+
return Effect.suspend(() => {
|
|
493
|
+
const files = /* @__PURE__ */ new Set();
|
|
494
|
+
const directories = /* @__PURE__ */ new Set();
|
|
495
|
+
return withTypescriptProjects({
|
|
496
|
+
configDirectory: layout.configDirectory,
|
|
497
|
+
serverTsconfigFilePath: layout.serverTsconfigFilePath,
|
|
498
|
+
clientTsconfigFilePath: layout.clientTsconfigFilePath,
|
|
499
|
+
fileSystem: {
|
|
500
|
+
readFile(filePath) {
|
|
501
|
+
files.add(normalizePath(filePath));
|
|
502
|
+
},
|
|
503
|
+
getAccessibleEntries(directory) {
|
|
504
|
+
directories.add(normalizePath(directory));
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
useFn(projects) {
|
|
508
|
+
return Effect.gen(function* () {
|
|
509
|
+
const inspection = yield* inspectProjects({
|
|
510
|
+
projects,
|
|
511
|
+
layout
|
|
512
|
+
});
|
|
513
|
+
const sources = yield* Effect.try({
|
|
514
|
+
try: () => {
|
|
515
|
+
return {
|
|
516
|
+
serverRootFiles: new Set(projects.server.rootFiles.map(normalizePath)),
|
|
517
|
+
clientRootFiles: new Set(projects.client.rootFiles.map(normalizePath)),
|
|
518
|
+
serverFiles: new Set(projects.server.program.getSourceFileNames().map(normalizePath)),
|
|
519
|
+
clientFiles: new Set(projects.client.program.getSourceFileNames().map(normalizePath))
|
|
520
|
+
};
|
|
521
|
+
},
|
|
522
|
+
catch: (cause) => {
|
|
523
|
+
return {
|
|
524
|
+
_tag: "TypescriptProjectError",
|
|
525
|
+
message: "Could not read TypeScript development inputs",
|
|
526
|
+
cause
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
return {
|
|
531
|
+
inspection,
|
|
532
|
+
inputs: {
|
|
533
|
+
...sources,
|
|
534
|
+
resolutionFiles: new Set([...files].filter((file) => {
|
|
535
|
+
return !sources.serverFiles.has(file) && !sources.clientFiles.has(file);
|
|
536
|
+
})),
|
|
537
|
+
directories
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
/** Watches native compiler inputs while keeping generated writes out of the event stream. */
|
|
546
|
+
function getDevelopmentSourceWatchPaths(input) {
|
|
547
|
+
return Effect.gen(function* () {
|
|
548
|
+
const path = yield* Path.Path;
|
|
549
|
+
const generatedDirectories = getGeneratedSourceDirectories(input.layout);
|
|
550
|
+
return [.../* @__PURE__ */ new Set([
|
|
551
|
+
...input.sources.serverFiles,
|
|
552
|
+
...input.sources.clientFiles,
|
|
553
|
+
...input.sources.serverRootFiles,
|
|
554
|
+
...input.sources.clientRootFiles,
|
|
555
|
+
...input.sources.resolutionFiles,
|
|
556
|
+
...input.sources.directories
|
|
557
|
+
])].filter((filePath) => {
|
|
558
|
+
return !generatedDirectories.some((directory) => {
|
|
559
|
+
const relativePath = path.relative(directory, filePath);
|
|
560
|
+
return containsRelativePath({
|
|
561
|
+
relativePath,
|
|
562
|
+
separator: path.sep,
|
|
563
|
+
absolute: path.isAbsolute(relativePath)
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
//#endregion
|
|
570
|
+
//#region src/build/development-generation.ts
|
|
571
|
+
/** Captures independent source and public inputs concurrently, without activating a runtime. */
|
|
572
|
+
function prepareDevelopmentGeneration(input) {
|
|
573
|
+
return Effect.gen(function* () {
|
|
574
|
+
const previous = input.previous;
|
|
575
|
+
const prepared = yield* Effect.all({
|
|
576
|
+
sources: previous === void 0 || previous.work.inspectSources ? inspectDevelopmentSources(input.layout) : Effect.succeed({
|
|
577
|
+
inspection: previous.generation.application.inspection,
|
|
578
|
+
inputs: previous.generation.sources
|
|
579
|
+
}),
|
|
580
|
+
publicAssets: previous === void 0 || previous.work.capturePublicAssets ? collectPublicAssets(input.layout) : Effect.succeed(previous.generation.application.publicAssets)
|
|
581
|
+
}, { concurrency: "unbounded" });
|
|
582
|
+
const application = yield* assembleApplicationGeneration({
|
|
583
|
+
sourceLayout: input.layout,
|
|
584
|
+
inspection: prepared.sources.inspection,
|
|
585
|
+
publicAssets: prepared.publicAssets
|
|
586
|
+
});
|
|
587
|
+
if (previous === void 0 || previous.work.inspectSources || previous.work.capturePublicAssets) yield* writeAndCheckApplicationGeneration(application);
|
|
588
|
+
return {
|
|
589
|
+
application,
|
|
590
|
+
sources: prepared.sources.inputs
|
|
591
|
+
};
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
//#endregion
|
|
595
|
+
//#region src/build/development-patterns.ts
|
|
596
|
+
/** Ordered inclusion and exclusion rules use native Vite glob matching. */
|
|
597
|
+
function compileDevelopmentPatterns(patterns) {
|
|
598
|
+
const rules = patterns.flatMap((raw) => {
|
|
599
|
+
const trimmed = raw.trim();
|
|
600
|
+
const excluded = trimmed.startsWith("!");
|
|
601
|
+
const source = excluded ? trimmed.slice(1).trim() : trimmed;
|
|
602
|
+
const anchored = source.startsWith("/") || source.startsWith("./");
|
|
603
|
+
let pattern = normalizePath(source);
|
|
604
|
+
if (pattern === "") return [];
|
|
605
|
+
const directory = pattern.endsWith("/");
|
|
606
|
+
pattern = pattern.replace(/\/$/, "");
|
|
607
|
+
pattern = pattern.replace(/^\.\//, "").replace(/^\//, "");
|
|
608
|
+
if (!anchored && !pattern.includes("/") && pattern !== ".") pattern = `**/${pattern}`;
|
|
609
|
+
const matchFn = createFilter(pattern, void 0, { resolve: false });
|
|
610
|
+
const prefix = pattern.split("/").filter((_segment, index, segments) => {
|
|
611
|
+
return segments.slice(0, index + 1).every((segment) => !/[*?{[]/.test(segment));
|
|
612
|
+
});
|
|
613
|
+
return [{
|
|
614
|
+
pattern,
|
|
615
|
+
directory,
|
|
616
|
+
excluded,
|
|
617
|
+
matchFn,
|
|
618
|
+
root: prefix.slice(0, -1).join("/") || "."
|
|
619
|
+
}];
|
|
620
|
+
});
|
|
621
|
+
return {
|
|
622
|
+
roots: rules.filter((rule) => !rule.excluded).map((rule) => rule.root),
|
|
623
|
+
matchesFn(relativePath) {
|
|
624
|
+
const segments = posix.normalize(normalizePath(relativePath)).split("/");
|
|
625
|
+
let matched = false;
|
|
626
|
+
for (const rule of rules) if (rule.pattern === "." || segments.some((_segment, index) => {
|
|
627
|
+
return (!rule.directory || index < segments.length - 1) && rule.matchFn(segments.slice(0, index + 1).join("/"));
|
|
628
|
+
})) matched = !rule.excluded;
|
|
629
|
+
return matched;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region src/build/development-server-process.ts
|
|
635
|
+
/** Reserves a port until the enclosing allocation scope closes. */
|
|
636
|
+
function getDevelopmentServerPort() {
|
|
637
|
+
return NodeSocketServer.make({
|
|
638
|
+
host: "127.0.0.1",
|
|
639
|
+
port: 0
|
|
640
|
+
}).pipe(Effect.map(({ address }) => {
|
|
641
|
+
if (address._tag === "UnixPathAddress") throw new Error("A TCP port probe returned a Unix socket address");
|
|
642
|
+
return address.port;
|
|
643
|
+
}));
|
|
644
|
+
}
|
|
645
|
+
/** Runs the authored server entry; its Effect scope owns process termination. */
|
|
646
|
+
function startDevelopmentServerProcess(input) {
|
|
647
|
+
return Effect.gen(function* () {
|
|
648
|
+
const process = yield* ChildProcess.make(globalThis.process.execPath, ["--enable-source-maps", input.layout.compiledServerEntryFilePath], {
|
|
649
|
+
env: { [DEVELOPMENT_PROTOCOL.serverPortEnvironmentVariable]: String(input.port) },
|
|
650
|
+
extendEnv: true,
|
|
651
|
+
stdin: "ignore",
|
|
652
|
+
stdout: "inherit",
|
|
653
|
+
stderr: "inherit",
|
|
654
|
+
forceKillAfter: "2 seconds"
|
|
655
|
+
});
|
|
656
|
+
const unexpectedExit = process.exitCode.pipe(Effect.flatMap((exitCode) => {
|
|
657
|
+
return Effect.fail({
|
|
658
|
+
_tag: "DevelopmentServerExit",
|
|
659
|
+
exitCode
|
|
660
|
+
});
|
|
661
|
+
}));
|
|
662
|
+
const origin = `http://127.0.0.1:${input.port}`;
|
|
663
|
+
const url = `${origin}${DEVELOPMENT_PROTOCOL.readinessPath}`;
|
|
664
|
+
yield* Effect.gen(function* () {
|
|
665
|
+
yield* (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatus((status) => status === 200)).get(url).pipe(Effect.flatMap((response) => response.text), Effect.timeout("1 second"), Effect.retry(Schedule.forever.pipe(Schedule.modifyDelay(({ output }) => {
|
|
666
|
+
return Effect.succeed(Math.min((output + 1) * 20, 200));
|
|
667
|
+
}))));
|
|
668
|
+
}).pipe(Effect.provide(FetchHttpClient.layer), Effect.timeoutOrElse({
|
|
669
|
+
duration: "10 seconds",
|
|
670
|
+
orElse: () => {
|
|
671
|
+
return Effect.fail({
|
|
672
|
+
_tag: "DevelopmentServerReadinessIssue",
|
|
673
|
+
url
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}), Effect.raceFirst(unexpectedExit));
|
|
677
|
+
return {
|
|
678
|
+
process,
|
|
679
|
+
origin,
|
|
680
|
+
unexpectedExit
|
|
681
|
+
};
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
//#endregion
|
|
685
|
+
//#region src/build/development-changes.ts
|
|
686
|
+
/**
|
|
687
|
+
* Batches native watcher events after 30 ms without another event. Completed
|
|
688
|
+
* batches accumulate while the consumer rebuilds; no changed path is discarded.
|
|
689
|
+
*/
|
|
690
|
+
function createDevelopmentChanges() {
|
|
691
|
+
return Effect.gen(function* () {
|
|
692
|
+
const notifications = yield* Queue.make();
|
|
693
|
+
const ready = yield* Queue.make({
|
|
694
|
+
capacity: 1,
|
|
695
|
+
strategy: "sliding"
|
|
696
|
+
});
|
|
697
|
+
let incoming = /* @__PURE__ */ new Set();
|
|
698
|
+
let pending = /* @__PURE__ */ new Set();
|
|
699
|
+
function recordChangeFn(filePath) {
|
|
700
|
+
Queue.offerUnsafe(notifications, normalizePath(filePath));
|
|
701
|
+
}
|
|
702
|
+
function recordErrorFn(cause) {
|
|
703
|
+
Queue.failCauseUnsafe(notifications, Cause.fail({
|
|
704
|
+
_tag: "DevelopmentWatchIssue",
|
|
705
|
+
cause
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
yield* Stream.fromQueue(notifications).pipe(Stream.tap((filePath) => Effect.sync(() => incoming.add(filePath))), Stream.debounce("30 millis"), Stream.runForEach(() => {
|
|
709
|
+
return Effect.sync(() => {
|
|
710
|
+
for (const filePath of incoming) pending.add(filePath);
|
|
711
|
+
incoming = /* @__PURE__ */ new Set();
|
|
712
|
+
Queue.offerUnsafe(ready, void 0);
|
|
713
|
+
});
|
|
714
|
+
}), Effect.catch((issue) => Queue.fail(ready, issue)), Effect.forkScoped);
|
|
715
|
+
return {
|
|
716
|
+
take: Effect.gen(function* () {
|
|
717
|
+
while (true) {
|
|
718
|
+
yield* Queue.take(ready);
|
|
719
|
+
if (pending.size > 0) {
|
|
720
|
+
const batch = pending;
|
|
721
|
+
pending = /* @__PURE__ */ new Set();
|
|
722
|
+
return batch;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}),
|
|
726
|
+
recordChangeFn,
|
|
727
|
+
recordErrorFn
|
|
728
|
+
};
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
//#endregion
|
|
732
|
+
//#region src/build/development-watcher.ts
|
|
733
|
+
function containsPath(input) {
|
|
734
|
+
const relativePath = relative(input.directory, input.filePath);
|
|
735
|
+
return containsRelativePath({
|
|
736
|
+
relativePath,
|
|
737
|
+
separator: sep,
|
|
738
|
+
absolute: isAbsolute(relativePath)
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
/** Application watching survives Vite restarts and failed replacement builds. */
|
|
742
|
+
function createDevelopmentWatcher(input) {
|
|
743
|
+
return Effect.gen(function* () {
|
|
744
|
+
const changes = yield* createDevelopmentChanges();
|
|
745
|
+
function matchesIgnoredPath(filePath) {
|
|
746
|
+
return /(?:^|[/\\])(?:node_modules|\.git)(?:[/\\]|$)/.test(filePath);
|
|
747
|
+
}
|
|
748
|
+
function acquireWatcher(plan) {
|
|
749
|
+
return Effect.gen(function* () {
|
|
750
|
+
const watcher = yield* Effect.acquireRelease(Effect.sync(() => {
|
|
751
|
+
return watch([...plan.paths], {
|
|
752
|
+
ignoreInitial: true,
|
|
753
|
+
ignored: (filePath) => {
|
|
754
|
+
return matchesIgnoredPath(filePath) || plan.generatedDirectories.some((directory) => {
|
|
755
|
+
return containsPath({
|
|
756
|
+
directory,
|
|
757
|
+
filePath
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
}), (watcher) => Effect.promise(() => watcher.close()));
|
|
763
|
+
watcher.on("all", (_event, filePath) => changes.recordChangeFn(filePath));
|
|
764
|
+
watcher.on("error", changes.recordErrorFn);
|
|
765
|
+
yield* Effect.callback((resume) => {
|
|
766
|
+
function onReady() {
|
|
767
|
+
resume(Effect.void);
|
|
768
|
+
}
|
|
769
|
+
function onError(cause) {
|
|
770
|
+
resume(Effect.fail({
|
|
771
|
+
_tag: "DevelopmentWatchIssue",
|
|
772
|
+
cause
|
|
773
|
+
}));
|
|
774
|
+
}
|
|
775
|
+
watcher.once("ready", onReady);
|
|
776
|
+
watcher.once("error", onError);
|
|
777
|
+
return Effect.sync(() => {
|
|
778
|
+
watcher.off("ready", onReady);
|
|
779
|
+
watcher.off("error", onError);
|
|
780
|
+
});
|
|
781
|
+
});
|
|
782
|
+
return plan;
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
const reference = yield* ScopedRef.fromAcquire(acquireWatcher({
|
|
786
|
+
paths: [dirname(input.configFilePath)],
|
|
787
|
+
generatedDirectories: input.generatedDirectories
|
|
788
|
+
}));
|
|
789
|
+
return {
|
|
790
|
+
...changes,
|
|
791
|
+
watchPathsFn(next) {
|
|
792
|
+
return Effect.gen(function* () {
|
|
793
|
+
const paths = [...new Set([dirname(input.configFilePath), ...next.paths].filter((filePath) => !matchesIgnoredPath(filePath)).map(normalizePath))].sort();
|
|
794
|
+
const plan = {
|
|
795
|
+
paths: paths.filter((filePath) => {
|
|
796
|
+
return !paths.some((directory) => {
|
|
797
|
+
return directory !== filePath && containsPath({
|
|
798
|
+
directory,
|
|
799
|
+
filePath
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
}),
|
|
803
|
+
generatedDirectories: [...next.generatedDirectories].sort()
|
|
804
|
+
};
|
|
805
|
+
if (JSON.stringify(yield* ScopedRef.get(reference)) !== JSON.stringify(plan)) yield* ScopedRef.set(reference, acquireWatcher(plan));
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
//#endregion
|
|
812
|
+
//#region src/build/development-work.ts
|
|
813
|
+
/** Classifies application work; ordinary browser modules remain Vite's HMR inputs. */
|
|
814
|
+
function classifyDevelopmentChanges(input) {
|
|
815
|
+
return Effect.gen(function* () {
|
|
816
|
+
const path = yield* Path.Path;
|
|
817
|
+
const { layout, inputs } = input;
|
|
818
|
+
const generated = getGeneratedSourceDirectories(layout);
|
|
819
|
+
function matchesGeneratedPath(filePath) {
|
|
820
|
+
return generated.some((directory) => {
|
|
821
|
+
return containsFilePath({
|
|
822
|
+
path,
|
|
823
|
+
directory,
|
|
824
|
+
filePath
|
|
825
|
+
});
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
const changedPaths = [...input.changedPaths].map(normalizePath).filter((filePath) => !matchesGeneratedPath(filePath));
|
|
829
|
+
const revalidate = compileDevelopmentPatterns(layout.dev?.revalidateClientOnChange ?? []);
|
|
830
|
+
let restartConfiguration = false;
|
|
831
|
+
let inspectSources = false;
|
|
832
|
+
let compileServer = false;
|
|
833
|
+
let capturePublicAssets = false;
|
|
834
|
+
let prepareCriticalCss = false;
|
|
835
|
+
let revalidateClient = false;
|
|
836
|
+
let checkProjectMembership = false;
|
|
837
|
+
for (const filePath of changedPaths) {
|
|
838
|
+
restartConfiguration ||= inputs.configurationFiles.has(filePath) || filePath === normalizePath(layout.configFilePath);
|
|
839
|
+
if (!inputs.configurationFiles.has(filePath) && inputs.viteConfigurationFiles.has(filePath)) continue;
|
|
840
|
+
const serverSource = inputs.sources.serverFiles.has(filePath) || inputs.sources.serverRootFiles.has(filePath);
|
|
841
|
+
const clientSource = inputs.sources.clientFiles.has(filePath) || inputs.sources.clientRootFiles.has(filePath);
|
|
842
|
+
const resolutionSource = inputs.sources.resolutionFiles.has(filePath);
|
|
843
|
+
inspectSources ||= serverSource || clientSource || resolutionSource;
|
|
844
|
+
compileServer ||= serverSource || resolutionSource || inputs.serverFiles.has(filePath);
|
|
845
|
+
capturePublicAssets ||= containsFilePath({
|
|
846
|
+
path,
|
|
847
|
+
directory: layout.publicAssetsSourceDirectory,
|
|
848
|
+
filePath
|
|
849
|
+
});
|
|
850
|
+
prepareCriticalCss ||= inputs.criticalCssFiles.has(filePath) || filePath === layout.criticalCssEntryFilePath;
|
|
851
|
+
revalidateClient ||= (layout.dev?.revalidateClientOnChange?.length ?? 0) > 0 && revalidate.matchesFn(path.relative(layout.configDirectory, filePath));
|
|
852
|
+
checkProjectMembership ||= !serverSource && !clientSource && !resolutionSource;
|
|
853
|
+
}
|
|
854
|
+
if (checkProjectMembership && !restartConfiguration) {
|
|
855
|
+
const roots = yield* readTypescriptProjectRoots(layout);
|
|
856
|
+
function differsFromPreviousFiles(previous, files) {
|
|
857
|
+
const next = new Set(files.map(normalizePath).filter((file) => !matchesGeneratedPath(file)));
|
|
858
|
+
const old = [...previous].filter((file) => !matchesGeneratedPath(file));
|
|
859
|
+
return old.length !== next.size || old.some((file) => !next.has(file));
|
|
860
|
+
}
|
|
861
|
+
const serverMembershipChanged = differsFromPreviousFiles(inputs.sources.serverRootFiles, roots.server);
|
|
862
|
+
const clientMembershipChanged = differsFromPreviousFiles(inputs.sources.clientRootFiles, roots.client);
|
|
863
|
+
inspectSources ||= serverMembershipChanged || clientMembershipChanged;
|
|
864
|
+
compileServer ||= serverMembershipChanged;
|
|
865
|
+
}
|
|
866
|
+
return {
|
|
867
|
+
restartConfiguration,
|
|
868
|
+
inspectSources,
|
|
869
|
+
compileServer,
|
|
870
|
+
capturePublicAssets,
|
|
871
|
+
prepareCriticalCss: layout.criticalCssEntryFilePath !== void 0 && (prepareCriticalCss || capturePublicAssets),
|
|
872
|
+
revalidateClient
|
|
873
|
+
};
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
function containsFilePath(input) {
|
|
877
|
+
const relativePath = input.path.relative(input.directory, input.filePath);
|
|
878
|
+
return containsRelativePath({
|
|
879
|
+
relativePath,
|
|
880
|
+
separator: input.path.sep,
|
|
881
|
+
absolute: input.path.isAbsolute(relativePath)
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
//#endregion
|
|
885
|
+
//#region src/build/load-config.ts
|
|
886
|
+
/** Vite's configuration loader recompiles configuration dependencies on each development restart. */
|
|
887
|
+
function loadDevelopmentConfig(configPath) {
|
|
888
|
+
return Effect.gen(function* () {
|
|
889
|
+
const configFilePath = (yield* Path.Path).resolve(configPath);
|
|
890
|
+
const loaded = yield* Effect.tryPromise({
|
|
891
|
+
try: () => {
|
|
892
|
+
return loadConfigFromFile({
|
|
893
|
+
command: "serve",
|
|
894
|
+
mode: "development"
|
|
895
|
+
}, configFilePath);
|
|
896
|
+
},
|
|
897
|
+
catch: (cause) => {
|
|
898
|
+
return {
|
|
899
|
+
_tag: "ConfigLoadError",
|
|
900
|
+
message: `Could not load Vorma config: ${configFilePath}`,
|
|
901
|
+
cause
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
return {
|
|
906
|
+
config: yield* Schema.decodeUnknownEffect(VormaConfigSchema)(loaded?.config),
|
|
907
|
+
configFilePath,
|
|
908
|
+
dependencies: loaded?.dependencies ?? []
|
|
909
|
+
};
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Imports the explicitly selected configuration using the CLI host's module
|
|
914
|
+
* loader and reports the absolute path of the file it was loaded from,
|
|
915
|
+
* because every configured path is relative to that file's directory.
|
|
916
|
+
*/
|
|
917
|
+
function loadVormaConfig(configPath) {
|
|
918
|
+
return Effect.gen(function* () {
|
|
919
|
+
const path = yield* Path.Path;
|
|
920
|
+
const absolutePath = path.resolve(configPath);
|
|
921
|
+
const url = yield* path.toFileUrl(absolutePath);
|
|
922
|
+
const module = yield* Effect.tryPromise({
|
|
923
|
+
try: () => import(url.href),
|
|
924
|
+
catch: (cause) => {
|
|
925
|
+
return {
|
|
926
|
+
_tag: "ConfigLoadError",
|
|
927
|
+
message: `Could not import Vorma config: ${absolutePath}`,
|
|
928
|
+
cause
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
return {
|
|
933
|
+
config: yield* Schema.decodeUnknownEffect(VormaConfigSchema)(module.default).pipe(Effect.mapError((cause) => {
|
|
934
|
+
return {
|
|
935
|
+
_tag: "ConfigLoadError",
|
|
936
|
+
message: `Expected a default-exported Vorma config in ${absolutePath}: ${cause.message}`,
|
|
937
|
+
cause
|
|
938
|
+
};
|
|
939
|
+
})),
|
|
940
|
+
configFilePath: absolutePath
|
|
941
|
+
};
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
//#endregion
|
|
945
|
+
//#region src/build/development.ts
|
|
946
|
+
/** Runs until interrupted; configuration replacement releases the complete previous session. */
|
|
947
|
+
function developApplication(configFilePath) {
|
|
948
|
+
return Effect.gen(function* () {
|
|
949
|
+
const { port, vitePort } = yield* Effect.scoped(Effect.all({
|
|
950
|
+
port: getDevelopmentServerPort(),
|
|
951
|
+
vitePort: getDevelopmentServerPort()
|
|
952
|
+
}));
|
|
953
|
+
let loaded = yield* loadDevelopmentConfig(configFilePath);
|
|
954
|
+
const resolved = yield* resolveVormaConfig(loaded);
|
|
955
|
+
const watcher = yield* createDevelopmentWatcher({
|
|
956
|
+
configFilePath: loaded.configFilePath,
|
|
957
|
+
generatedDirectories: getGeneratedSourceDirectories(resolved)
|
|
958
|
+
});
|
|
959
|
+
let initialized = false;
|
|
960
|
+
while (true) {
|
|
961
|
+
let ready = false;
|
|
962
|
+
const next = yield* Effect.scoped(runDevelopmentSession({
|
|
963
|
+
loaded,
|
|
964
|
+
port,
|
|
965
|
+
vitePort,
|
|
966
|
+
watcher,
|
|
967
|
+
onReadyFn: () => {
|
|
968
|
+
ready = true;
|
|
969
|
+
initialized = true;
|
|
970
|
+
}
|
|
971
|
+
})).pipe(Effect.result);
|
|
972
|
+
if (Result.isSuccess(next)) {
|
|
973
|
+
loaded = next.success;
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
if (!initialized || ready) return yield* Effect.fail(next.failure);
|
|
977
|
+
yield* Effect.logError(next.failure);
|
|
978
|
+
while (true) {
|
|
979
|
+
yield* watcher.take;
|
|
980
|
+
const corrected = yield* loadDevelopmentConfig(configFilePath).pipe(Effect.result);
|
|
981
|
+
if (Result.isSuccess(corrected)) {
|
|
982
|
+
loaded = corrected.success;
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
yield* Effect.logError(corrected.failure);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}).pipe(Effect.scoped);
|
|
989
|
+
}
|
|
990
|
+
function runDevelopmentSession(input) {
|
|
991
|
+
return Effect.gen(function* () {
|
|
992
|
+
const scope = yield* Effect.scope;
|
|
993
|
+
const path = yield* Path.Path;
|
|
994
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
995
|
+
const resolved = yield* resolveVormaConfig(input.loaded);
|
|
996
|
+
yield* input.watcher.watchPathsFn({
|
|
997
|
+
paths: [
|
|
998
|
+
...input.loaded.dependencies,
|
|
999
|
+
path.dirname(resolved.serverEntryFilePath),
|
|
1000
|
+
path.dirname(resolved.clientEntryFilePath),
|
|
1001
|
+
path.dirname(resolved.serverTsconfigFilePath),
|
|
1002
|
+
path.dirname(resolved.clientTsconfigFilePath),
|
|
1003
|
+
resolved.publicAssetsSourceDirectory,
|
|
1004
|
+
...resolved.criticalCssEntryFilePath === void 0 ? [] : [path.dirname(resolved.criticalCssEntryFilePath)]
|
|
1005
|
+
],
|
|
1006
|
+
generatedDirectories: getGeneratedSourceDirectories(resolved)
|
|
1007
|
+
});
|
|
1008
|
+
const layout = yield* loadApplicationSourceLayout(resolved);
|
|
1009
|
+
yield* acquireOutputOwnership({
|
|
1010
|
+
lockDirectory: layout.clientGeneratedDirectory,
|
|
1011
|
+
boundaryDirectories: [layout.serverGeneratedDirectory],
|
|
1012
|
+
kind: "dev"
|
|
1013
|
+
});
|
|
1014
|
+
let retiredDirectories = [];
|
|
1015
|
+
yield* Effect.addFinalizer(() => {
|
|
1016
|
+
return Effect.forEach(retiredDirectories, (directory) => {
|
|
1017
|
+
return Effect.gen(function* () {
|
|
1018
|
+
const names = yield* fileSystem.readDirectory(directory);
|
|
1019
|
+
yield* Effect.forEach(names.filter((name) => {
|
|
1020
|
+
return name !== "build.lock" && name !== ".gitignore";
|
|
1021
|
+
}), (name) => {
|
|
1022
|
+
const filePath = path.join(directory, name);
|
|
1023
|
+
return checkOutputPath({
|
|
1024
|
+
filePath,
|
|
1025
|
+
rootDirectory: directory
|
|
1026
|
+
}).pipe(Effect.andThen(fileSystem.remove(filePath, { recursive: true })));
|
|
1027
|
+
}, {
|
|
1028
|
+
concurrency: 16,
|
|
1029
|
+
discard: true
|
|
1030
|
+
});
|
|
1031
|
+
});
|
|
1032
|
+
}, { discard: true }).pipe(Effect.orDie);
|
|
1033
|
+
});
|
|
1034
|
+
yield* preparePublicOutput(layout);
|
|
1035
|
+
const generation = yield* prepareDevelopmentGeneration({ layout });
|
|
1036
|
+
const client = yield* startDevelopmentClient(toDevelopmentClientInputs(generation.application, generation.sources), input.vitePort, input.watcher.recordChangeFn);
|
|
1037
|
+
let build = yield* prepareDevelopmentBuild({
|
|
1038
|
+
generation,
|
|
1039
|
+
client
|
|
1040
|
+
});
|
|
1041
|
+
let head = yield* client.readHeadHtml;
|
|
1042
|
+
let clientConfigurationRevision = client.configurationRevision;
|
|
1043
|
+
let artifacts = assembleDevelopmentArtifacts({
|
|
1044
|
+
build,
|
|
1045
|
+
client,
|
|
1046
|
+
developmentHeadHtml: head
|
|
1047
|
+
});
|
|
1048
|
+
let inputs;
|
|
1049
|
+
const pendingPaths = /* @__PURE__ */ new Set();
|
|
1050
|
+
let revision = 0;
|
|
1051
|
+
const restart = yield* Deferred.make();
|
|
1052
|
+
const fatal = yield* Deferred.make();
|
|
1053
|
+
let application;
|
|
1054
|
+
const stopApplication = Effect.suspend(() => {
|
|
1055
|
+
const previous = application;
|
|
1056
|
+
application = void 0;
|
|
1057
|
+
if (previous === void 0) return Effect.void;
|
|
1058
|
+
previous.stopping = true;
|
|
1059
|
+
return Fiber.interrupt(previous.fiber);
|
|
1060
|
+
});
|
|
1061
|
+
yield* Effect.addFinalizer(() => stopApplication);
|
|
1062
|
+
const startApplication = Effect.gen(function* () {
|
|
1063
|
+
const ready = yield* Deferred.make();
|
|
1064
|
+
let announced = false;
|
|
1065
|
+
const running = {
|
|
1066
|
+
stopping: false,
|
|
1067
|
+
fiber: void 0
|
|
1068
|
+
};
|
|
1069
|
+
const fiber = yield* Effect.scoped(Effect.gen(function* () {
|
|
1070
|
+
const server = yield* startDevelopmentServerProcess({
|
|
1071
|
+
layout,
|
|
1072
|
+
port: input.port
|
|
1073
|
+
});
|
|
1074
|
+
announced = true;
|
|
1075
|
+
yield* Deferred.succeed(ready, void 0);
|
|
1076
|
+
return yield* server.unexpectedExit;
|
|
1077
|
+
})).pipe(Effect.tapCause((cause) => {
|
|
1078
|
+
return Deferred.failCause(ready, cause).pipe(Effect.andThen(() => {
|
|
1079
|
+
return announced && !running.stopping ? Deferred.failCause(fatal, cause) : Effect.void;
|
|
1080
|
+
}));
|
|
1081
|
+
}), Effect.forkIn(scope));
|
|
1082
|
+
running.fiber = fiber;
|
|
1083
|
+
application = {
|
|
1084
|
+
fiber,
|
|
1085
|
+
get stopping() {
|
|
1086
|
+
return running.stopping;
|
|
1087
|
+
},
|
|
1088
|
+
set stopping(value) {
|
|
1089
|
+
running.stopping = value;
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
yield* Deferred.await(ready).pipe(Effect.onExit((exit) => Exit.isFailure(exit) ? stopApplication : Effect.void));
|
|
1093
|
+
});
|
|
1094
|
+
function notifyFn(message) {
|
|
1095
|
+
client.server.ws.send({
|
|
1096
|
+
type: "custom",
|
|
1097
|
+
event: "vorma:development",
|
|
1098
|
+
data: message
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
function watchInputs(next) {
|
|
1102
|
+
return Effect.gen(function* () {
|
|
1103
|
+
inputs = {
|
|
1104
|
+
sources: next.generation.sources,
|
|
1105
|
+
serverFiles: new Set(next.server.inputFilePaths.map(normalizePath)),
|
|
1106
|
+
criticalCssFiles: new Set(next.stylesheet.inputFilePaths.map(normalizePath)),
|
|
1107
|
+
configurationFiles: new Set(input.loaded.dependencies.map(normalizePath)),
|
|
1108
|
+
viteConfigurationFiles: new Set(client.server.config.configFileDependencies)
|
|
1109
|
+
};
|
|
1110
|
+
const sourcePaths = yield* getDevelopmentSourceWatchPaths({
|
|
1111
|
+
layout,
|
|
1112
|
+
sources: inputs.sources
|
|
1113
|
+
});
|
|
1114
|
+
const dataRoots = compileDevelopmentPatterns(layout.dev?.revalidateClientOnChange ?? []).roots.map((root) => path.resolve(layout.configDirectory, root));
|
|
1115
|
+
yield* input.watcher.watchPathsFn({
|
|
1116
|
+
paths: [
|
|
1117
|
+
...sourcePaths,
|
|
1118
|
+
...inputs.serverFiles,
|
|
1119
|
+
...inputs.criticalCssFiles,
|
|
1120
|
+
...inputs.configurationFiles,
|
|
1121
|
+
layout.configFilePath,
|
|
1122
|
+
layout.viteConfigFilePath,
|
|
1123
|
+
layout.publicAssetsSourceDirectory,
|
|
1124
|
+
...dataRoots
|
|
1125
|
+
],
|
|
1126
|
+
generatedDirectories: getGeneratedSourceDirectories(layout)
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
yield* publishApplicationArtifacts({
|
|
1131
|
+
generation: generation.application,
|
|
1132
|
+
server: build.server,
|
|
1133
|
+
artifacts
|
|
1134
|
+
});
|
|
1135
|
+
yield* startApplication;
|
|
1136
|
+
yield* client.listen;
|
|
1137
|
+
yield* watchInputs(build);
|
|
1138
|
+
input.onReadyFn();
|
|
1139
|
+
yield* Console.log(`Vorma development: http://127.0.0.1:${input.port}`);
|
|
1140
|
+
const rebuild = Effect.suspend(() => {
|
|
1141
|
+
const preparingRevision = revision;
|
|
1142
|
+
return Effect.gen(function* () {
|
|
1143
|
+
const incoming = yield* classifyDevelopmentChanges({
|
|
1144
|
+
layout,
|
|
1145
|
+
inputs: {
|
|
1146
|
+
...inputs,
|
|
1147
|
+
viteConfigurationFiles: new Set(client.server.config.configFileDependencies)
|
|
1148
|
+
},
|
|
1149
|
+
changedPaths: new Set(pendingPaths)
|
|
1150
|
+
});
|
|
1151
|
+
const work = {
|
|
1152
|
+
...incoming,
|
|
1153
|
+
prepareCriticalCss: incoming.prepareCriticalCss || client.configurationRevision !== clientConfigurationRevision
|
|
1154
|
+
};
|
|
1155
|
+
if (!Object.values(work).some(Boolean)) {
|
|
1156
|
+
if (preparingRevision === revision) pendingPaths.clear();
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
if (work.compileServer || work.restartConfiguration) notifyFn({ kind: "rebuilding" });
|
|
1160
|
+
if (work.restartConfiguration) {
|
|
1161
|
+
const next = yield* loadDevelopmentConfig(layout.configFilePath);
|
|
1162
|
+
const nextLayout = yield* resolveVormaConfig(next).pipe(Effect.flatMap(loadApplicationSourceLayout));
|
|
1163
|
+
const nextDirectories = new Set(getGeneratedSourceDirectories(nextLayout));
|
|
1164
|
+
retiredDirectories = [...new Set(getGeneratedSourceDirectories(layout))].filter((directory) => !nextDirectories.has(directory));
|
|
1165
|
+
yield* Deferred.succeed(restart, next);
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
const [next] = yield* Effect.all([prepareDevelopmentGeneration({
|
|
1169
|
+
layout,
|
|
1170
|
+
previous: {
|
|
1171
|
+
generation: build.generation,
|
|
1172
|
+
work
|
|
1173
|
+
}
|
|
1174
|
+
}).pipe(Effect.flatMap((generation) => {
|
|
1175
|
+
return prepareDevelopmentBuild({
|
|
1176
|
+
generation,
|
|
1177
|
+
client,
|
|
1178
|
+
previous: {
|
|
1179
|
+
build,
|
|
1180
|
+
work
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
})), work.compileServer ? stopApplication : Effect.void], { concurrency: 2 });
|
|
1184
|
+
const previousArtifacts = artifacts;
|
|
1185
|
+
head = yield* client.readHeadHtml;
|
|
1186
|
+
const nextArtifacts = assembleDevelopmentArtifacts({
|
|
1187
|
+
build: next,
|
|
1188
|
+
client,
|
|
1189
|
+
developmentHeadHtml: head
|
|
1190
|
+
});
|
|
1191
|
+
const restartVite = work.compileServer || JSON.stringify(previousArtifacts.metadata.clientViews) !== JSON.stringify(nextArtifacts.metadata.clientViews);
|
|
1192
|
+
const reload = restartVite || previousArtifacts.metadata.clientBuildId !== nextArtifacts.metadata.clientBuildId;
|
|
1193
|
+
yield* publishApplicationArtifacts({
|
|
1194
|
+
generation: next.generation.application,
|
|
1195
|
+
artifacts: nextArtifacts,
|
|
1196
|
+
...work.compileServer ? { server: next.server } : {}
|
|
1197
|
+
});
|
|
1198
|
+
if (work.compileServer) yield* startApplication;
|
|
1199
|
+
client.resumeInspectionFn(reload);
|
|
1200
|
+
if (restartVite) {
|
|
1201
|
+
yield* client.restart;
|
|
1202
|
+
head = yield* client.readHeadHtml;
|
|
1203
|
+
}
|
|
1204
|
+
yield* watchInputs(next);
|
|
1205
|
+
if (preparingRevision !== revision) return;
|
|
1206
|
+
build = next;
|
|
1207
|
+
clientConfigurationRevision = client.configurationRevision;
|
|
1208
|
+
artifacts = nextArtifacts;
|
|
1209
|
+
pendingPaths.clear();
|
|
1210
|
+
const retainedFiles = new Set(artifacts.metadata.publicFiles.map((file) => file.fileName));
|
|
1211
|
+
yield* Effect.forEach(yield* fileSystem.readDirectory(layout.publicDirectory), (fileName) => {
|
|
1212
|
+
return retainedFiles.has(fileName) ? Effect.void : fileSystem.remove(path.join(layout.publicDirectory, fileName));
|
|
1213
|
+
}, {
|
|
1214
|
+
concurrency: 16,
|
|
1215
|
+
discard: true
|
|
1216
|
+
});
|
|
1217
|
+
if (!restartVite && reload) client.server.ws.send({ type: "full-reload" });
|
|
1218
|
+
else {
|
|
1219
|
+
if (previousArtifacts.metadata.criticalCss !== artifacts.metadata.criticalCss) notifyFn({
|
|
1220
|
+
kind: "css",
|
|
1221
|
+
css: artifacts.metadata.criticalCss
|
|
1222
|
+
});
|
|
1223
|
+
notifyFn({ kind: work.revalidateClient ? "revalidate" : "ready" });
|
|
1224
|
+
}
|
|
1225
|
+
}).pipe(Effect.catchCause((cause) => {
|
|
1226
|
+
if (Cause.hasInterruptsOnly(cause)) return Effect.failCause(cause);
|
|
1227
|
+
const message = Cause.pretty(cause);
|
|
1228
|
+
notifyFn({
|
|
1229
|
+
kind: "error",
|
|
1230
|
+
message
|
|
1231
|
+
});
|
|
1232
|
+
return Effect.logError(message);
|
|
1233
|
+
}));
|
|
1234
|
+
});
|
|
1235
|
+
const changes = Stream.fromEffectRepeat(input.watcher.take).pipe(Stream.tap((incoming) => {
|
|
1236
|
+
return Effect.sync(() => {
|
|
1237
|
+
for (const filePath of incoming) pendingPaths.add(filePath);
|
|
1238
|
+
revision++;
|
|
1239
|
+
});
|
|
1240
|
+
}), Stream.switchMap(() => Stream.fromEffect(rebuild)), Stream.runDrain);
|
|
1241
|
+
return yield* Deferred.await(restart).pipe(Effect.raceFirst(changes.pipe(Effect.andThen(Effect.never))), Effect.raceFirst(Deferred.await(fatal)));
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
//#endregion
|
|
1245
|
+
//#region src/build/cli.ts
|
|
1246
|
+
const buildCommand = Command.make("build", { config: Flag.File("config", { mustExist: true }).pipe(Flag.withDescription("Path to the application's default-exported Vorma configuration.")) }, ({ config }) => {
|
|
1247
|
+
return Effect.gen(function* () {
|
|
1248
|
+
const loaded = yield* loadVormaConfig(config);
|
|
1249
|
+
const result = yield* buildApplication(loaded);
|
|
1250
|
+
yield* Console.log(`Published Vorma build ${result.clientBuildId}`);
|
|
1251
|
+
});
|
|
1252
|
+
}).pipe(Command.withDescription("Build and publish production application artifacts."));
|
|
1253
|
+
const nodeServices = Layer.provideMerge(NodeChildProcessSpawner.layer, Layer.mergeAll(NodeFileSystem.layer, NodeCrypto.layer, NodePath.layer, NodeStdio.layer, NodeTerminal.layer));
|
|
1254
|
+
const developmentCommand = Command.make("dev", { config: Flag.File("config", { mustExist: true }).pipe(Flag.withDescription("Path to the application's Vorma configuration.")) }, ({ config }) => developApplication(config)).pipe(Command.withDescription("Run application development with rebuilds and browser hot updates."));
|
|
1255
|
+
Command.make("vorma").pipe(Command.withSubcommands([buildCommand, developmentCommand]), Command.run({ version })).pipe(Effect.provide(nodeServices), NodeRuntime.runMain);
|
|
1256
|
+
//#endregion
|