@ersc/vercel 0.1.3
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 +21 -0
- package/README.md +20 -0
- package/dist/build.d.ts +2 -0
- package/dist/build.js +27 -0
- package/dist/build.js.map +1 -0
- package/dist/package.d.ts +13 -0
- package/dist/package.js +221 -0
- package/dist/package.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Nikhil S
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @ersc/vercel
|
|
2
|
+
|
|
3
|
+
Vercel deployment packaging for effective-rsc on Bun 1.4+.
|
|
4
|
+
|
|
5
|
+
Add `@ersc/vercel` to the app's `devDependencies` and build with:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
ersc build --adapter @ersc/vercel
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This generates `.vercel/output/` with a Bun function, traced dependencies, and copied assets.
|
|
12
|
+
No custom server entry or `vercel.json` is needed. Connect the repository in the Vercel dashboard
|
|
13
|
+
with the Other preset. See the [deployment guide](https://effective-rsc.nikhilsnayak.dev/docs/guides/deploying-to-vercel)
|
|
14
|
+
for build settings and monorepos.
|
|
15
|
+
|
|
16
|
+
Public asset symlinks are copied as content, including targets outside the app. Broken or cyclic
|
|
17
|
+
links fail packaging. Computed file reads may escape tracing; local databases are not persistent.
|
|
18
|
+
Plain `ersc build` skips packaging and leaves previous deployment output untouched.
|
|
19
|
+
|
|
20
|
+
See the [hello-world example](https://github.com/nikhilsnayak/effective-rsc/tree/main/examples/hello-world).
|
package/dist/build.d.ts
ADDED
package/dist/build.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { VercelBuildError, packageForVercel } from "./package.js";
|
|
3
|
+
|
|
4
|
+
import * as __rspack_external__effect_platform_bun_BunServices_112addf2 from "@effect/platform-bun/BunServices";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const build = Effect.fn('@ersc/vercel/build')(function*(context) {
|
|
12
|
+
const serverEntry = yield* Effect["try"]({
|
|
13
|
+
try: ()=>Bun.resolveSync('effective-rsc/server', context.root),
|
|
14
|
+
catch: (cause)=>new VercelBuildError({
|
|
15
|
+
message: 'Cannot resolve effective-rsc/server from the application.',
|
|
16
|
+
cause
|
|
17
|
+
})
|
|
18
|
+
});
|
|
19
|
+
yield* packageForVercel({
|
|
20
|
+
...context,
|
|
21
|
+
serverEntry
|
|
22
|
+
});
|
|
23
|
+
}, Effect.provide(__rspack_external__effect_platform_bun_BunServices_112addf2.layer));
|
|
24
|
+
|
|
25
|
+
export { build };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sources":["../src/build.ts"],"sourcesContent":["import * as BunServices from '@effect/platform-bun/BunServices';\nimport { Effect } from 'effect';\nimport type { BuildHook } from 'effective-rsc/build';\n\nimport { packageForVercel, VercelBuildError } from './package';\n\nexport const build: BuildHook = Effect.fn('@ersc/vercel/build')(function* (context) {\n const serverEntry = yield* Effect.try({\n try: () => Bun.resolveSync('effective-rsc/server', context.root),\n catch: (cause) =>\n new VercelBuildError({\n message: 'Cannot resolve effective-rsc/server from the application.',\n cause,\n }),\n });\n yield* packageForVercel({ ...context, serverEntry });\n}, Effect.provide(BunServices.layer));\n"],"names":["BunServices","Effect","packageForVercel","VercelBuildError","build","context","serverEntry","Bun","cause"],"mappings":";;;;;;;AAAgE;AAChC;AAG+B;AAExD,MAAMI,KAAKA,GAAcH,SAAS,CAAC,sBAAsB,UAAWI,OAAO;IAChF,MAAMC,cAAc,OAAOL,aAAU,CAAC;QACpC,KAAK,IAAMM,IAAI,WAAW,CAAC,wBAAwBF,QAAQ,IAAI;QAC/D,OAAO,CAACG,QACN,IAAIL,gBAAgBA,CAAC;gBACnB,SAAS;gBACTK;YACF;IACJ;IACA,OAAON,gBAAgBA,CAAC;QAAE,GAAGG,OAAO;QAAEC;IAAY;AACpD,GAAGL,cAAc,CAACD,iEAAiB,GAAG"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path, Schema } from 'effect';
|
|
2
|
+
import type { BuildContext } from 'effective-rsc/build';
|
|
3
|
+
declare const VercelBuildError_base: Schema.Class<VercelBuildError, Schema.TaggedStruct<"VercelBuildError", {
|
|
4
|
+
readonly message: Schema.String;
|
|
5
|
+
readonly cause: Schema.Defect;
|
|
6
|
+
}>, import("effect/Cause").YieldableError>;
|
|
7
|
+
export declare class VercelBuildError extends VercelBuildError_base {
|
|
8
|
+
}
|
|
9
|
+
export declare const commonDirectory: (path: Path.Path, applicationRoot: string, serverEntry: string) => string;
|
|
10
|
+
export declare const packageForVercel: (args_0: BuildContext & {
|
|
11
|
+
readonly serverEntry: string;
|
|
12
|
+
}) => Effect.Effect<void, import("effect/PlatformError").PlatformError | VercelBuildError, FileSystem.FileSystem | Path.Path>;
|
|
13
|
+
export {};
|
package/dist/package.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { lstat } from "node:fs/promises";
|
|
2
|
+
import { nodeFileTrace } from "@vercel/nft";
|
|
3
|
+
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// FileSystem.stat follows symlinks; packaging needs to preserve the links themselves.
|
|
9
|
+
// oxlint-disable-next-line effecttsgo/node-builtin-import
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class VercelBuildError extends Schema.TaggedError()('VercelBuildError', {
|
|
14
|
+
message: Schema.String,
|
|
15
|
+
cause: Schema.Defect()
|
|
16
|
+
}) {
|
|
17
|
+
}
|
|
18
|
+
const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
|
|
19
|
+
const commonDirectory = (path, applicationRoot, serverEntry)=>{
|
|
20
|
+
let base = path.resolve(applicationRoot);
|
|
21
|
+
while(path.relative(base, serverEntry).split(path.sep).includes('..')){
|
|
22
|
+
base = path.dirname(base);
|
|
23
|
+
}
|
|
24
|
+
return base;
|
|
25
|
+
};
|
|
26
|
+
const traceDependencies = Effect.fnUntraced(function*(applicationRoot, frameworkEntry, entries) {
|
|
27
|
+
const path = yield* Path.Path;
|
|
28
|
+
// Files outside NFT's base are omitted: https://github.com/vercel/nft#base
|
|
29
|
+
let base = commonDirectory(path, applicationRoot, frameworkEntry);
|
|
30
|
+
while(true){
|
|
31
|
+
const trace = yield* Effect.tryPromise({
|
|
32
|
+
try: ()=>nodeFileTrace([
|
|
33
|
+
...entries
|
|
34
|
+
], {
|
|
35
|
+
base,
|
|
36
|
+
processCwd: applicationRoot,
|
|
37
|
+
// Custom conditions replace NFT's defaults: https://github.com/vercel/nft#exports--imports
|
|
38
|
+
conditions: [
|
|
39
|
+
'bun',
|
|
40
|
+
'node',
|
|
41
|
+
'node-addons'
|
|
42
|
+
],
|
|
43
|
+
mixedModules: true
|
|
44
|
+
}),
|
|
45
|
+
catch: (cause)=>new VercelBuildError({
|
|
46
|
+
message: 'Failed to trace deployment dependencies.',
|
|
47
|
+
cause
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
if (trace.warnings.size > 0) {
|
|
51
|
+
return yield* new VercelBuildError({
|
|
52
|
+
message: 'Could not trace every deployment dependency.',
|
|
53
|
+
cause: new AggregateError(trace.warnings)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
let expandedBase = base;
|
|
57
|
+
// NFT silently ignores dependencies outside base, without emitting a warning.
|
|
58
|
+
for (const [file, reason] of trace.reasons){
|
|
59
|
+
if (reason.ignored) {
|
|
60
|
+
expandedBase = commonDirectory(path, expandedBase, path.resolve(base, file));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (expandedBase === base) {
|
|
64
|
+
return {
|
|
65
|
+
base,
|
|
66
|
+
files: trace.fileList
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
base = expandedBase;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
const copyAssets = Effect.fnUntraced(function*(source, destination, ancestors) {
|
|
73
|
+
const fs = yield* FileSystem.FileSystem;
|
|
74
|
+
const path = yield* Path.Path;
|
|
75
|
+
// Copy asset link targets; their original locations will not exist after deployment.
|
|
76
|
+
const resolved = yield* fs.realPath(source);
|
|
77
|
+
const info = yield* fs.stat(resolved);
|
|
78
|
+
if (info.type === 'Directory') {
|
|
79
|
+
if (destination === resolved || destination.startsWith(`${resolved}${path.sep}`)) {
|
|
80
|
+
return yield* new VercelBuildError({
|
|
81
|
+
message: `Asset directory ${source} contains the deployment destination.`,
|
|
82
|
+
cause: resolved
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (ancestors.includes(resolved)) {
|
|
86
|
+
return yield* new VercelBuildError({
|
|
87
|
+
message: `Asset directory ${source} contains a cyclic symlink.`,
|
|
88
|
+
cause: resolved
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
yield* fs.makeDirectory(destination, {
|
|
92
|
+
recursive: true
|
|
93
|
+
});
|
|
94
|
+
const entries = yield* fs.readDirectory(resolved);
|
|
95
|
+
for (const entry of entries){
|
|
96
|
+
yield* copyAssets(path.join(resolved, entry), path.join(destination, entry), [
|
|
97
|
+
...ancestors,
|
|
98
|
+
resolved
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
} else if (info.type === 'File') {
|
|
102
|
+
yield* fs.copyFile(resolved, destination);
|
|
103
|
+
} else {
|
|
104
|
+
return yield* new VercelBuildError({
|
|
105
|
+
message: `Asset ${source} is not a regular file or directory.`,
|
|
106
|
+
cause: info.type
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}, Effect.mapError((cause)=>new VercelBuildError({
|
|
110
|
+
message: 'Failed to copy deployment assets.',
|
|
111
|
+
cause
|
|
112
|
+
})));
|
|
113
|
+
const packageForVercel = Effect.fn('@ersc/vercel/packageForVercel')(function*({ root, serverDir, clientDir, publicDir, serverEntry }) {
|
|
114
|
+
const fs = yield* FileSystem.FileSystem;
|
|
115
|
+
const path = yield* Path.Path;
|
|
116
|
+
const applicationRoot = yield* fs.realPath(root);
|
|
117
|
+
const frameworkEntry = yield* fs.realPath(serverEntry);
|
|
118
|
+
const bootDirectory = path.join(applicationRoot, '.vercel/ersc');
|
|
119
|
+
const bootFile = path.join(bootDirectory, 'server.mjs');
|
|
120
|
+
const output = path.join(applicationRoot, '.vercel/output');
|
|
121
|
+
// A .func directory must contain every runtime file: https://vercel.com/docs/build-output-api/primitives#functions
|
|
122
|
+
const functionDirectory = path.join(output, 'functions/index.func');
|
|
123
|
+
const moduleSpecifier = path.relative(bootDirectory, frameworkEntry).split(path.sep).join('/');
|
|
124
|
+
yield* fs.makeDirectory(bootDirectory, {
|
|
125
|
+
recursive: true
|
|
126
|
+
});
|
|
127
|
+
// Bun.serve entrypoints: https://vercel.com/docs/functions/runtimes/bun#deploy-with-the-bun-framework-preset
|
|
128
|
+
// App-relative file reads: https://vercel.com/kb/guide/how-can-i-use-files-in-serverless-functions
|
|
129
|
+
// Change cwd before importing: module initialization may read application files.
|
|
130
|
+
yield* fs.writeFileString(bootFile, `import { fileURLToPath } from 'node:url';
|
|
131
|
+
const root = fileURLToPath(new URL('../../', import.meta.url));
|
|
132
|
+
process.chdir(root);
|
|
133
|
+
const { start } = await import(${encodeJson(moduleSpecifier.startsWith('.') ? moduleSpecifier : `./${moduleSpecifier}`)});
|
|
134
|
+
// Hostname and port are local-only; Vercel handles incoming traffic.
|
|
135
|
+
await start({ root, hostname: 'localhost', port: 18193 });
|
|
136
|
+
`);
|
|
137
|
+
// Seed every server chunk; runtime-selected imports may be invisible to tracing.
|
|
138
|
+
const serverFiles = yield* fs.readDirectory(serverDir, {
|
|
139
|
+
recursive: true
|
|
140
|
+
});
|
|
141
|
+
const { base, files } = yield* traceDependencies(applicationRoot, frameworkEntry, [
|
|
142
|
+
bootFile,
|
|
143
|
+
...serverFiles.filter((file)=>file.endsWith('.js')).map((file)=>path.join(serverDir, file))
|
|
144
|
+
]);
|
|
145
|
+
// Keep the previous output if dependency tracing fails.
|
|
146
|
+
yield* fs.remove(output, {
|
|
147
|
+
recursive: true,
|
|
148
|
+
force: true
|
|
149
|
+
});
|
|
150
|
+
yield* fs.makeDirectory(functionDirectory, {
|
|
151
|
+
recursive: true
|
|
152
|
+
});
|
|
153
|
+
yield* Effect.forEach(files, (file)=>Effect.gen(function*() {
|
|
154
|
+
const source = path.join(base, file);
|
|
155
|
+
const destination = path.join(functionDirectory, file);
|
|
156
|
+
const info = yield* Effect.tryPromise({
|
|
157
|
+
try: ()=>lstat(source),
|
|
158
|
+
catch: (cause)=>new VercelBuildError({
|
|
159
|
+
message: `Failed to inspect ${file}.`,
|
|
160
|
+
cause
|
|
161
|
+
})
|
|
162
|
+
});
|
|
163
|
+
yield* fs.makeDirectory(path.dirname(destination), {
|
|
164
|
+
recursive: true
|
|
165
|
+
});
|
|
166
|
+
if (info.isSymbolicLink()) {
|
|
167
|
+
const target = yield* fs.readLink(source);
|
|
168
|
+
const targetPath = path.resolve(path.dirname(source), target);
|
|
169
|
+
const relativeTarget = path.relative(base, targetPath);
|
|
170
|
+
if (relativeTarget.split(path.sep).includes('..')) {
|
|
171
|
+
return yield* new VercelBuildError({
|
|
172
|
+
message: `Dependency ${file} points outside the deployment root.`,
|
|
173
|
+
cause: target
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// Rebase links onto the copied tree, including originally absolute links.
|
|
177
|
+
yield* fs.symlink(path.relative(path.dirname(destination), path.join(functionDirectory, relativeTarget)), destination);
|
|
178
|
+
} else if (info.isFile()) {
|
|
179
|
+
yield* fs.copyFile(source, destination);
|
|
180
|
+
}
|
|
181
|
+
}));
|
|
182
|
+
const deployedRoot = path.join(functionDirectory, path.relative(base, applicationRoot));
|
|
183
|
+
for (const source of [
|
|
184
|
+
clientDir,
|
|
185
|
+
publicDir
|
|
186
|
+
]){
|
|
187
|
+
// Directory entries distinguish missing assets from broken symlinks.
|
|
188
|
+
const entries = yield* fs.readDirectory(path.dirname(source));
|
|
189
|
+
if (entries.includes(path.basename(source))) {
|
|
190
|
+
const destination = path.join(deployedRoot, path.relative(root, source));
|
|
191
|
+
// Remove any traced symlink before copying files into this location.
|
|
192
|
+
yield* fs.remove(destination, {
|
|
193
|
+
recursive: true,
|
|
194
|
+
force: true
|
|
195
|
+
});
|
|
196
|
+
yield* copyAssets(source, destination, []);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Function config and launcher: https://vercel.com/docs/build-output-api/primitives#serverless-function-configuration
|
|
200
|
+
yield* fs.writeFileString(path.join(functionDirectory, '.vc-config.json'), encodeJson({
|
|
201
|
+
// Bun runtime series: https://vercel.com/docs/functions/runtimes/bun#configuring-the-runtime
|
|
202
|
+
runtime: 'bun1.4.x',
|
|
203
|
+
handler: path.relative(base, bootFile).split(path.sep).join('/'),
|
|
204
|
+
launcherType: 'Nodejs',
|
|
205
|
+
shouldAddHelpers: false
|
|
206
|
+
}));
|
|
207
|
+
// Route all methods through the app: https://vercel.com/docs/build-output-api/configuration#routes
|
|
208
|
+
yield* fs.writeFileString(path.join(output, 'config.json'), encodeJson({
|
|
209
|
+
version: 3,
|
|
210
|
+
routes: [
|
|
211
|
+
{
|
|
212
|
+
src: '/(.*)',
|
|
213
|
+
dest: '/index'
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}));
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
export { VercelBuildError, commonDirectory, packageForVercel };
|
|
220
|
+
|
|
221
|
+
//# sourceMappingURL=package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","sources":["../src/package.ts"],"sourcesContent":["// FileSystem.stat follows symlinks; packaging needs to preserve the links themselves.\n// oxlint-disable-next-line effecttsgo/node-builtin-import\nimport { lstat } from 'node:fs/promises';\n\nimport { nodeFileTrace } from '@vercel/nft';\nimport { Effect, FileSystem, Path, Schema } from 'effect';\nimport type { BuildContext } from 'effective-rsc/build';\n\nexport class VercelBuildError extends Schema.TaggedError<VercelBuildError>()('VercelBuildError', {\n message: Schema.String,\n cause: Schema.Defect(),\n}) {}\n\nconst encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));\n\nexport const commonDirectory = (\n path: Path.Path,\n applicationRoot: string,\n serverEntry: string,\n): string => {\n let base = path.resolve(applicationRoot);\n while (path.relative(base, serverEntry).split(path.sep).includes('..')) {\n base = path.dirname(base);\n }\n return base;\n};\n\nconst traceDependencies = Effect.fnUntraced(function* (\n applicationRoot: string,\n frameworkEntry: string,\n entries: ReadonlyArray<string>,\n) {\n const path = yield* Path.Path;\n // Files outside NFT's base are omitted: https://github.com/vercel/nft#base\n let base = commonDirectory(path, applicationRoot, frameworkEntry);\n while (true) {\n const trace = yield* Effect.tryPromise({\n try: () =>\n nodeFileTrace([...entries], {\n base,\n processCwd: applicationRoot,\n // Custom conditions replace NFT's defaults: https://github.com/vercel/nft#exports--imports\n conditions: ['bun', 'node', 'node-addons'],\n mixedModules: true,\n }),\n catch: (cause) =>\n new VercelBuildError({ message: 'Failed to trace deployment dependencies.', cause }),\n });\n if (trace.warnings.size > 0) {\n return yield* new VercelBuildError({\n message: 'Could not trace every deployment dependency.',\n cause: new AggregateError(trace.warnings),\n });\n }\n let expandedBase = base;\n // NFT silently ignores dependencies outside base, without emitting a warning.\n for (const [file, reason] of trace.reasons) {\n if (reason.ignored) {\n expandedBase = commonDirectory(path, expandedBase, path.resolve(base, file));\n }\n }\n if (expandedBase === base) {\n return { base, files: trace.fileList };\n }\n base = expandedBase;\n }\n});\n\nconst copyAssets: (\n source: string,\n destination: string,\n ancestors: ReadonlyArray<string>,\n) => Effect.Effect<void, VercelBuildError, FileSystem.FileSystem | Path.Path> = Effect.fnUntraced(\n function* (source, destination, ancestors) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n // Copy asset link targets; their original locations will not exist after deployment.\n const resolved = yield* fs.realPath(source);\n const info = yield* fs.stat(resolved);\n if (info.type === 'Directory') {\n if (destination === resolved || destination.startsWith(`${resolved}${path.sep}`)) {\n return yield* new VercelBuildError({\n message: `Asset directory ${source} contains the deployment destination.`,\n cause: resolved,\n });\n }\n if (ancestors.includes(resolved)) {\n return yield* new VercelBuildError({\n message: `Asset directory ${source} contains a cyclic symlink.`,\n cause: resolved,\n });\n }\n yield* fs.makeDirectory(destination, { recursive: true });\n const entries = yield* fs.readDirectory(resolved);\n for (const entry of entries) {\n yield* copyAssets(path.join(resolved, entry), path.join(destination, entry), [\n ...ancestors,\n resolved,\n ]);\n }\n } else if (info.type === 'File') {\n yield* fs.copyFile(resolved, destination);\n } else {\n return yield* new VercelBuildError({\n message: `Asset ${source} is not a regular file or directory.`,\n cause: info.type,\n });\n }\n },\n Effect.mapError(\n (cause) => new VercelBuildError({ message: 'Failed to copy deployment assets.', cause }),\n ),\n);\n\nexport const packageForVercel = Effect.fn('@ersc/vercel/packageForVercel')(function* ({\n root,\n serverDir,\n clientDir,\n publicDir,\n serverEntry,\n}: BuildContext & {\n readonly serverEntry: string;\n}) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const applicationRoot = yield* fs.realPath(root);\n const frameworkEntry = yield* fs.realPath(serverEntry);\n const bootDirectory = path.join(applicationRoot, '.vercel/ersc');\n const bootFile = path.join(bootDirectory, 'server.mjs');\n const output = path.join(applicationRoot, '.vercel/output');\n // A .func directory must contain every runtime file: https://vercel.com/docs/build-output-api/primitives#functions\n const functionDirectory = path.join(output, 'functions/index.func');\n const moduleSpecifier = path.relative(bootDirectory, frameworkEntry).split(path.sep).join('/');\n\n yield* fs.makeDirectory(bootDirectory, { recursive: true });\n // Bun.serve entrypoints: https://vercel.com/docs/functions/runtimes/bun#deploy-with-the-bun-framework-preset\n // App-relative file reads: https://vercel.com/kb/guide/how-can-i-use-files-in-serverless-functions\n // Change cwd before importing: module initialization may read application files.\n yield* fs.writeFileString(\n bootFile,\n `import { fileURLToPath } from 'node:url';\nconst root = fileURLToPath(new URL('../../', import.meta.url));\nprocess.chdir(root);\nconst { start } = await import(${encodeJson(moduleSpecifier.startsWith('.') ? moduleSpecifier : `./${moduleSpecifier}`)});\n// Hostname and port are local-only; Vercel handles incoming traffic.\nawait start({ root, hostname: 'localhost', port: 18193 });\n`,\n );\n\n // Seed every server chunk; runtime-selected imports may be invisible to tracing.\n const serverFiles = yield* fs.readDirectory(serverDir, { recursive: true });\n const { base, files } = yield* traceDependencies(applicationRoot, frameworkEntry, [\n bootFile,\n ...serverFiles.filter((file) => file.endsWith('.js')).map((file) => path.join(serverDir, file)),\n ]);\n\n // Keep the previous output if dependency tracing fails.\n yield* fs.remove(output, { recursive: true, force: true });\n yield* fs.makeDirectory(functionDirectory, { recursive: true });\n yield* Effect.forEach(files, (file) =>\n Effect.gen(function* () {\n const source = path.join(base, file);\n const destination = path.join(functionDirectory, file);\n const info = yield* Effect.tryPromise({\n try: () => lstat(source),\n catch: (cause) => new VercelBuildError({ message: `Failed to inspect ${file}.`, cause }),\n });\n yield* fs.makeDirectory(path.dirname(destination), { recursive: true });\n if (info.isSymbolicLink()) {\n const target = yield* fs.readLink(source);\n const targetPath = path.resolve(path.dirname(source), target);\n const relativeTarget = path.relative(base, targetPath);\n if (relativeTarget.split(path.sep).includes('..')) {\n return yield* new VercelBuildError({\n message: `Dependency ${file} points outside the deployment root.`,\n cause: target,\n });\n }\n // Rebase links onto the copied tree, including originally absolute links.\n yield* fs.symlink(\n path.relative(path.dirname(destination), path.join(functionDirectory, relativeTarget)),\n destination,\n );\n } else if (info.isFile()) {\n yield* fs.copyFile(source, destination);\n }\n }),\n );\n\n const deployedRoot = path.join(functionDirectory, path.relative(base, applicationRoot));\n for (const source of [clientDir, publicDir]) {\n // Directory entries distinguish missing assets from broken symlinks.\n const entries = yield* fs.readDirectory(path.dirname(source));\n if (entries.includes(path.basename(source))) {\n const destination = path.join(deployedRoot, path.relative(root, source));\n // Remove any traced symlink before copying files into this location.\n yield* fs.remove(destination, { recursive: true, force: true });\n yield* copyAssets(source, destination, []);\n }\n }\n // Function config and launcher: https://vercel.com/docs/build-output-api/primitives#serverless-function-configuration\n yield* fs.writeFileString(\n path.join(functionDirectory, '.vc-config.json'),\n encodeJson({\n // Bun runtime series: https://vercel.com/docs/functions/runtimes/bun#configuring-the-runtime\n runtime: 'bun1.4.x',\n handler: path.relative(base, bootFile).split(path.sep).join('/'),\n launcherType: 'Nodejs',\n shouldAddHelpers: false,\n }),\n );\n // Route all methods through the app: https://vercel.com/docs/build-output-api/configuration#routes\n yield* fs.writeFileString(\n path.join(output, 'config.json'),\n encodeJson({ version: 3, routes: [{ src: '/(.*)', dest: '/index' }] }),\n );\n});\n"],"names":["lstat","nodeFileTrace","Effect","FileSystem","Path","Schema","VercelBuildError","encodeJson","commonDirectory","path","applicationRoot","serverEntry","base","traceDependencies","frameworkEntry","entries","trace","cause","AggregateError","expandedBase","file","reason","copyAssets","source","destination","ancestors","fs","resolved","info","entry","packageForVercel","root","serverDir","clientDir","publicDir","bootDirectory","bootFile","output","functionDirectory","moduleSpecifier","serverFiles","files","target","targetPath","relativeTarget","deployedRoot"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ersc/vercel",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Vercel deployment adapter for effective-rsc",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bun",
|
|
7
|
+
"effect",
|
|
8
|
+
"react-server-components",
|
|
9
|
+
"vercel"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/nikhilsnayak/effective-rsc/tree/main/packages/vercel#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/nikhilsnayak/effective-rsc/issues"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/nikhilsnayak/effective-rsc.git",
|
|
19
|
+
"directory": "packages/vercel"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
"./build": {
|
|
29
|
+
"types": "./dist/build.d.ts",
|
|
30
|
+
"default": "./dist/build.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "rslib build",
|
|
38
|
+
"prepack": "bun run build",
|
|
39
|
+
"test": "bun run --bun vitest run"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@vercel/nft": "1.11.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@effect/platform-bun": "4.0.0-rc.112",
|
|
46
|
+
"@effect/vitest": "4.0.0-rc.112",
|
|
47
|
+
"@rslib/core": "1.0.0",
|
|
48
|
+
"@types/bun": "^1.4.1",
|
|
49
|
+
"effect": "4.0.0-rc.112",
|
|
50
|
+
"effective-rsc": "0.1.3",
|
|
51
|
+
"typescript": "7.0.2",
|
|
52
|
+
"vitest": "^4.1.11"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@effect/platform-bun": "4.0.0-rc.112",
|
|
56
|
+
"effect": "4.0.0-rc.112",
|
|
57
|
+
"effective-rsc": "0.1.3"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"bun": ">=1.4.0"
|
|
61
|
+
}
|
|
62
|
+
}
|