@vercel/ruby 2.4.0-canary.20260211174907.cdd2da6 → 2.5.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/dist/index.js +95 -49
- package/package.json +9 -6
- package/vc_init.rb +12 -2
- package/vc_init_dev.rb +21 -3
- package/vc_utils.rb +101 -0
package/dist/index.js
CHANGED
|
@@ -2371,9 +2371,9 @@ var require_end_of_stream = __commonJS({
|
|
|
2371
2371
|
}
|
|
2372
2372
|
});
|
|
2373
2373
|
|
|
2374
|
-
// ../../node_modules/.pnpm/pump@3.0.
|
|
2374
|
+
// ../../node_modules/.pnpm/pump@3.0.4/node_modules/pump/index.js
|
|
2375
2375
|
var require_pump = __commonJS({
|
|
2376
|
-
"../../node_modules/.pnpm/pump@3.0.
|
|
2376
|
+
"../../node_modules/.pnpm/pump@3.0.4/node_modules/pump/index.js"(exports, module2) {
|
|
2377
2377
|
var once = require_once();
|
|
2378
2378
|
var eos = require_end_of_stream();
|
|
2379
2379
|
var fs;
|
|
@@ -2383,7 +2383,7 @@ var require_pump = __commonJS({
|
|
|
2383
2383
|
}
|
|
2384
2384
|
var noop = function() {
|
|
2385
2385
|
};
|
|
2386
|
-
var ancient = /^v?\.0/.test(process.version);
|
|
2386
|
+
var ancient = typeof process === "undefined" ? false : /^v?\.0/.test(process.version);
|
|
2387
2387
|
var isFn = function(fn) {
|
|
2388
2388
|
return typeof fn === "function";
|
|
2389
2389
|
};
|
|
@@ -6940,6 +6940,7 @@ var require_semver2 = __commonJS({
|
|
|
6940
6940
|
var src_exports = {};
|
|
6941
6941
|
__export(src_exports, {
|
|
6942
6942
|
build: () => build,
|
|
6943
|
+
diagnostics: () => diagnostics,
|
|
6943
6944
|
shouldServe: () => shouldServe,
|
|
6944
6945
|
startDevServer: () => startDevServer,
|
|
6945
6946
|
version: () => version
|
|
@@ -6949,7 +6950,7 @@ var import_os2 = require("os");
|
|
|
6949
6950
|
var import_path3 = require("path");
|
|
6950
6951
|
var import_execa2 = __toESM(require_execa());
|
|
6951
6952
|
var import_fs_extra = __toESM(require_lib());
|
|
6952
|
-
var
|
|
6953
|
+
var import_build_utils4 = require("@vercel/build-utils");
|
|
6953
6954
|
|
|
6954
6955
|
// src/install-ruby.ts
|
|
6955
6956
|
var import_execa = __toESM(require_execa());
|
|
@@ -7156,12 +7157,16 @@ function isInstalled({ major, minor }) {
|
|
|
7156
7157
|
return Boolean(import_which.default.sync((0, import_path.join)(gemHome, "bin/ruby"), { nothrow: true })) && Boolean(import_which.default.sync((0, import_path.join)(gemHome, "bin/gem"), { nothrow: true }));
|
|
7157
7158
|
}
|
|
7158
7159
|
|
|
7160
|
+
// src/diagnostics.ts
|
|
7161
|
+
var import_build_utils2 = require("@vercel/build-utils");
|
|
7162
|
+
var diagnostics = (0, import_build_utils2.createDiagnostics)("ruby");
|
|
7163
|
+
|
|
7159
7164
|
// src/start-dev-server.ts
|
|
7160
7165
|
var import_child_process2 = require("child_process");
|
|
7161
7166
|
var import_fs = require("fs");
|
|
7162
7167
|
var import_path2 = require("path");
|
|
7163
7168
|
var import_which2 = __toESM(require_lib2());
|
|
7164
|
-
var
|
|
7169
|
+
var import_build_utils3 = require("@vercel/build-utils");
|
|
7165
7170
|
function silenceNodeWarnings() {
|
|
7166
7171
|
const original = process.emitWarning.bind(
|
|
7167
7172
|
process
|
|
@@ -7199,12 +7204,12 @@ function installGlobalCleanupHandlers() {
|
|
|
7199
7204
|
try {
|
|
7200
7205
|
process.kill(info.pid, "SIGTERM");
|
|
7201
7206
|
} catch (err) {
|
|
7202
|
-
(0,
|
|
7207
|
+
(0, import_build_utils3.debug)(`Error sending SIGTERM to ${info.pid}: ${err}`);
|
|
7203
7208
|
}
|
|
7204
7209
|
try {
|
|
7205
7210
|
process.kill(info.pid, "SIGKILL");
|
|
7206
7211
|
} catch (err) {
|
|
7207
|
-
(0,
|
|
7212
|
+
(0, import_build_utils3.debug)(`Error sending SIGKILL to ${info.pid}: ${err}`);
|
|
7208
7213
|
}
|
|
7209
7214
|
PERSISTENT_SERVERS.delete(key);
|
|
7210
7215
|
}
|
|
@@ -7212,7 +7217,7 @@ function installGlobalCleanupHandlers() {
|
|
|
7212
7217
|
try {
|
|
7213
7218
|
restoreWarnings();
|
|
7214
7219
|
} catch (err) {
|
|
7215
|
-
(0,
|
|
7220
|
+
(0, import_build_utils3.debug)(`Error restoring warnings: ${err}`);
|
|
7216
7221
|
}
|
|
7217
7222
|
restoreWarnings = null;
|
|
7218
7223
|
}
|
|
@@ -7232,14 +7237,18 @@ function createDevRubyShim(workPath, entrypoint) {
|
|
|
7232
7237
|
const vercelRubyDir = (0, import_path2.join)(workPath, ".vercel", "ruby");
|
|
7233
7238
|
(0, import_fs.mkdirSync)(vercelRubyDir, { recursive: true });
|
|
7234
7239
|
const shimPath = (0, import_path2.join)(vercelRubyDir, `vc_init_dev.rb`);
|
|
7240
|
+
const utilsPath = (0, import_path2.join)(vercelRubyDir, "vc__utils__ruby.rb");
|
|
7235
7241
|
const templatePath = (0, import_path2.join)(__dirname, "..", "vc_init_dev.rb");
|
|
7242
|
+
const utilsTemplatePath = (0, import_path2.join)(__dirname, "..", "vc_utils.rb");
|
|
7236
7243
|
const template = (0, import_fs.readFileSync)(templatePath, "utf8");
|
|
7244
|
+
const utilsTemplate = (0, import_fs.readFileSync)(utilsTemplatePath, "utf8");
|
|
7237
7245
|
const shimSource = template.replace(/__VC_DEV_ENTRYPOINT__/g, entrypoint);
|
|
7238
7246
|
(0, import_fs.writeFileSync)(shimPath, shimSource, "utf8");
|
|
7239
|
-
(0,
|
|
7247
|
+
(0, import_fs.writeFileSync)(utilsPath, utilsTemplate, "utf8");
|
|
7248
|
+
(0, import_build_utils3.debug)(`Prepared Ruby dev shim at ${shimPath}`);
|
|
7240
7249
|
return shimPath;
|
|
7241
7250
|
} catch (err) {
|
|
7242
|
-
(0,
|
|
7251
|
+
(0, import_build_utils3.debug)(`Failed to prepare Ruby dev shim: ${err?.message || err}`);
|
|
7243
7252
|
return null;
|
|
7244
7253
|
}
|
|
7245
7254
|
}
|
|
@@ -7260,8 +7269,22 @@ async function run(cmd, args, opts) {
|
|
|
7260
7269
|
env: opts.env,
|
|
7261
7270
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7262
7271
|
});
|
|
7263
|
-
child.stdout?.on("data", (
|
|
7264
|
-
|
|
7272
|
+
child.stdout?.on("data", (data) => {
|
|
7273
|
+
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
7274
|
+
if (opts.onStdout) {
|
|
7275
|
+
opts.onStdout(chunk);
|
|
7276
|
+
} else {
|
|
7277
|
+
process.stdout.write(chunk.toString());
|
|
7278
|
+
}
|
|
7279
|
+
});
|
|
7280
|
+
child.stderr?.on("data", (data) => {
|
|
7281
|
+
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
7282
|
+
if (opts.onStderr) {
|
|
7283
|
+
opts.onStderr(chunk);
|
|
7284
|
+
} else {
|
|
7285
|
+
process.stderr.write(chunk.toString());
|
|
7286
|
+
}
|
|
7287
|
+
});
|
|
7265
7288
|
child.on("close", (code, signal) => resolve({ code, signal }));
|
|
7266
7289
|
});
|
|
7267
7290
|
}
|
|
@@ -7309,7 +7332,7 @@ var startDevServer = async (opts) => {
|
|
|
7309
7332
|
const shimPath = createDevRubyShim(workPath, entrypoint);
|
|
7310
7333
|
if (!shimPath) {
|
|
7311
7334
|
rejectChildReady(
|
|
7312
|
-
new
|
|
7335
|
+
new import_build_utils3.NowBuildError({
|
|
7313
7336
|
code: "RUBY_DEV_SHIM_ERROR",
|
|
7314
7337
|
message: "Failed to create Ruby dev shim."
|
|
7315
7338
|
})
|
|
@@ -7325,11 +7348,16 @@ var startDevServer = async (opts) => {
|
|
|
7325
7348
|
}
|
|
7326
7349
|
const checkDeps = async () => {
|
|
7327
7350
|
if (gemfile && bundlerPath) {
|
|
7328
|
-
(0,
|
|
7351
|
+
(0, import_build_utils3.debug)(`Running "bundle check" for ${gemfile}`);
|
|
7329
7352
|
const check = await run(
|
|
7330
7353
|
bundlerPath,
|
|
7331
7354
|
["check", "--gemfile", gemfile],
|
|
7332
|
-
{
|
|
7355
|
+
{
|
|
7356
|
+
cwd: projectDir,
|
|
7357
|
+
env,
|
|
7358
|
+
onStdout: opts.onStdout,
|
|
7359
|
+
onStderr: opts.onStderr
|
|
7360
|
+
}
|
|
7333
7361
|
);
|
|
7334
7362
|
if (check.code !== 0) {
|
|
7335
7363
|
return false;
|
|
@@ -7339,7 +7367,7 @@ var startDevServer = async (opts) => {
|
|
|
7339
7367
|
};
|
|
7340
7368
|
checkDeps().then((ok) => {
|
|
7341
7369
|
if (!ok) {
|
|
7342
|
-
const err = new
|
|
7370
|
+
const err = new import_build_utils3.NowBuildError({
|
|
7343
7371
|
code: "RUBY_GEMS_MISSING",
|
|
7344
7372
|
message: 'Required gems are not installed. Run "vercel build" and try again.'
|
|
7345
7373
|
});
|
|
@@ -7352,25 +7380,25 @@ var startDevServer = async (opts) => {
|
|
|
7352
7380
|
cmd = bundlerPath;
|
|
7353
7381
|
args = ["exec", "ruby", shimPath];
|
|
7354
7382
|
}
|
|
7355
|
-
(0,
|
|
7383
|
+
(0, import_build_utils3.debug)(`Starting Ruby dev server: ${cmd} ${args.join(" ")}`);
|
|
7356
7384
|
const child = (0, import_child_process2.spawn)(cmd, args, {
|
|
7357
7385
|
cwd: workPath,
|
|
7358
7386
|
env,
|
|
7359
|
-
stdio: ["
|
|
7387
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7360
7388
|
});
|
|
7361
7389
|
childProcess = child;
|
|
7362
7390
|
stdoutLogListener = (buf) => {
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7391
|
+
if (opts.onStdout) {
|
|
7392
|
+
opts.onStdout(buf);
|
|
7393
|
+
} else {
|
|
7394
|
+
process.stdout.write(buf.toString());
|
|
7367
7395
|
}
|
|
7368
7396
|
};
|
|
7369
7397
|
stderrLogListener = (buf) => {
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7398
|
+
if (opts.onStderr) {
|
|
7399
|
+
opts.onStderr(buf);
|
|
7400
|
+
} else {
|
|
7401
|
+
process.stderr.write(buf.toString());
|
|
7374
7402
|
}
|
|
7375
7403
|
};
|
|
7376
7404
|
child.stdout?.on("data", stdoutLogListener);
|
|
@@ -7448,7 +7476,7 @@ async function matchPaths(configPatterns, workPath) {
|
|
|
7448
7476
|
}
|
|
7449
7477
|
const patternPaths = await Promise.all(
|
|
7450
7478
|
patterns.map(async (pattern) => {
|
|
7451
|
-
const files = await (0,
|
|
7479
|
+
const files = await (0, import_build_utils4.glob)(pattern, workPath);
|
|
7452
7480
|
return Object.keys(files);
|
|
7453
7481
|
})
|
|
7454
7482
|
);
|
|
@@ -7477,8 +7505,8 @@ async function prepareGemfile(gemfilePath, major) {
|
|
|
7477
7505
|
return { modified };
|
|
7478
7506
|
}
|
|
7479
7507
|
async function bundleLock(bundlerPath, gemfilePath, rubyPath) {
|
|
7480
|
-
const bundleAppConfig = await (0,
|
|
7481
|
-
const bundlerEnv = (0,
|
|
7508
|
+
const bundleAppConfig = await (0, import_build_utils4.getWriteableDirectory)();
|
|
7509
|
+
const bundlerEnv = (0, import_build_utils4.cloneEnv)(process.env, {
|
|
7482
7510
|
PATH: `${(0, import_path3.dirname)(rubyPath)}:${(0, import_path3.dirname)(bundlerPath)}:${process.env.PATH}`,
|
|
7483
7511
|
BUNDLE_SILENCE_ROOT_WARNING: "1",
|
|
7484
7512
|
BUNDLE_APP_CONFIG: bundleAppConfig,
|
|
@@ -7493,7 +7521,7 @@ async function bundleLock(bundlerPath, gemfilePath, rubyPath) {
|
|
|
7493
7521
|
const darwinPlatform = darwinMajor ? `${darwinArchToken}-darwin-${darwinMajor}` : `${darwinArchToken}-darwin`;
|
|
7494
7522
|
platforms.push(darwinPlatform);
|
|
7495
7523
|
}
|
|
7496
|
-
(0,
|
|
7524
|
+
(0, import_build_utils4.debug)(`ruby: ensuring Gemfile.lock has platforms ${platforms.join(", ")}`);
|
|
7497
7525
|
const lockArgs = ["lock"];
|
|
7498
7526
|
for (const platform of platforms) {
|
|
7499
7527
|
lockArgs.push("--add-platform", platform);
|
|
@@ -7511,8 +7539,8 @@ async function bundleLock(bundlerPath, gemfilePath, rubyPath) {
|
|
|
7511
7539
|
}
|
|
7512
7540
|
}
|
|
7513
7541
|
async function bundleInstall(bundlePath, bundleDir, gemfilePath, rubyPath) {
|
|
7514
|
-
const bundleAppConfig = await (0,
|
|
7515
|
-
const bundlerEnv = (0,
|
|
7542
|
+
const bundleAppConfig = await (0, import_build_utils4.getWriteableDirectory)();
|
|
7543
|
+
const bundlerEnv = (0, import_build_utils4.cloneEnv)(process.env, {
|
|
7516
7544
|
PATH: `${(0, import_path3.dirname)(rubyPath)}:${(0, import_path3.dirname)(bundlePath)}:${process.env.PATH}`,
|
|
7517
7545
|
BUNDLE_SILENCE_ROOT_WARNING: "1",
|
|
7518
7546
|
BUNDLE_APP_CONFIG: bundleAppConfig,
|
|
@@ -7521,7 +7549,7 @@ async function bundleInstall(bundlePath, bundleDir, gemfilePath, rubyPath) {
|
|
|
7521
7549
|
BUNDLE_PATH: bundleDir,
|
|
7522
7550
|
BUNDLE_FROZEN: "true"
|
|
7523
7551
|
});
|
|
7524
|
-
(0,
|
|
7552
|
+
(0, import_build_utils4.debug)('running "bundle install"');
|
|
7525
7553
|
const installRes = await (0, import_execa2.default)(
|
|
7526
7554
|
bundlePath,
|
|
7527
7555
|
["install", "--gemfile", gemfilePath],
|
|
@@ -7543,12 +7571,13 @@ var build = async ({
|
|
|
7543
7571
|
files,
|
|
7544
7572
|
entrypoint,
|
|
7545
7573
|
config,
|
|
7546
|
-
meta = {}
|
|
7574
|
+
meta = {},
|
|
7575
|
+
service
|
|
7547
7576
|
}) => {
|
|
7548
|
-
await (0,
|
|
7577
|
+
await (0, import_build_utils4.download)(files, workPath, meta);
|
|
7549
7578
|
const entrypointFsDirname = (0, import_path3.join)(workPath, (0, import_path3.dirname)(entrypoint));
|
|
7550
7579
|
const gemfileName = "Gemfile";
|
|
7551
|
-
let gemfilePath = await (0,
|
|
7580
|
+
let gemfilePath = await (0, import_build_utils4.walkParentDirs)({
|
|
7552
7581
|
base: workPath,
|
|
7553
7582
|
start: entrypointFsDirname,
|
|
7554
7583
|
filename: gemfileName
|
|
@@ -7561,17 +7590,17 @@ var build = async ({
|
|
|
7561
7590
|
const { gemHome, bundlerPath, vendorPath, runtime, rubyPath, major } = await installBundler(meta, gemfileContents);
|
|
7562
7591
|
process.env.GEM_HOME = gemHome;
|
|
7563
7592
|
try {
|
|
7564
|
-
(0,
|
|
7593
|
+
(0, import_build_utils4.debug)("preparing Gemfile");
|
|
7565
7594
|
await prepareGemfile(gemfilePath, major);
|
|
7566
|
-
(0,
|
|
7595
|
+
(0, import_build_utils4.debug)('running "bundle lock"');
|
|
7567
7596
|
await bundleLock(bundlerPath, gemfilePath, rubyPath);
|
|
7568
7597
|
} catch (err) {
|
|
7569
|
-
(0,
|
|
7598
|
+
(0, import_build_utils4.debug)(
|
|
7570
7599
|
"failed to normalize Gemfile/lockfile before vendor check",
|
|
7571
7600
|
err
|
|
7572
7601
|
);
|
|
7573
7602
|
}
|
|
7574
|
-
(0,
|
|
7603
|
+
(0, import_build_utils4.debug)(`Checking existing vendor directory at "${vendorPath}"`);
|
|
7575
7604
|
const vendorDir = (0, import_path3.join)(workPath, vendorPath);
|
|
7576
7605
|
const bundleDir = (0, import_path3.join)(workPath, "vendor", "bundle");
|
|
7577
7606
|
const relativeVendorDir = (0, import_path3.join)(entrypointFsDirname, vendorPath);
|
|
@@ -7579,31 +7608,34 @@ var build = async ({
|
|
|
7579
7608
|
const hasRelativeVendorDir = await (0, import_fs_extra.pathExists)(relativeVendorDir);
|
|
7580
7609
|
if (hasRelativeVendorDir) {
|
|
7581
7610
|
if (hasRootVendorDir) {
|
|
7582
|
-
(0,
|
|
7611
|
+
(0, import_build_utils4.debug)(
|
|
7583
7612
|
"found two vendor directories, choosing the vendor directory relative to entrypoint"
|
|
7584
7613
|
);
|
|
7585
7614
|
} else {
|
|
7586
|
-
(0,
|
|
7615
|
+
(0, import_build_utils4.debug)("found vendor directory relative to entrypoint");
|
|
7587
7616
|
}
|
|
7588
7617
|
await (0, import_fs_extra.move)(relativeVendorDir, vendorDir);
|
|
7589
7618
|
} else if (hasRootVendorDir) {
|
|
7590
|
-
(0,
|
|
7619
|
+
(0, import_build_utils4.debug)("found vendor directory in project root");
|
|
7591
7620
|
}
|
|
7592
7621
|
await (0, import_fs_extra.ensureDir)(vendorDir);
|
|
7593
7622
|
await bundleInstall(bundlerPath, bundleDir, gemfilePath, rubyPath);
|
|
7594
7623
|
try {
|
|
7595
7624
|
await (0, import_fs_extra.remove)((0, import_path3.join)(vendorDir, "cache"));
|
|
7596
|
-
} catch (
|
|
7625
|
+
} catch (_e) {
|
|
7597
7626
|
}
|
|
7598
7627
|
const originalRbPath = (0, import_path3.join)(__dirname, "..", "vc_init.rb");
|
|
7599
7628
|
const originalHandlerRbContents = await (0, import_fs_extra.readFile)(originalRbPath, "utf8");
|
|
7600
|
-
(0,
|
|
7629
|
+
const originalUtilsRbPath = (0, import_path3.join)(__dirname, "..", "vc_utils.rb");
|
|
7630
|
+
const originalUtilsRbContents = await (0, import_fs_extra.readFile)(originalUtilsRbPath, "utf8");
|
|
7631
|
+
(0, import_build_utils4.debug)("entrypoint is", entrypoint);
|
|
7601
7632
|
const userHandlerFilePath = entrypoint.replace(/\.rb$/, "");
|
|
7602
7633
|
const nowHandlerRbContents = originalHandlerRbContents.replace(
|
|
7603
7634
|
/__VC_HANDLER_FILENAME/g,
|
|
7604
7635
|
userHandlerFilePath
|
|
7605
7636
|
);
|
|
7606
7637
|
const handlerRbFilename = "vc__handler__ruby";
|
|
7638
|
+
const utilsRbFilename = "vc__utils__ruby.rb";
|
|
7607
7639
|
const predefinedExcludes = [
|
|
7608
7640
|
".git/**",
|
|
7609
7641
|
".gitignore",
|
|
@@ -7617,10 +7649,13 @@ var build = async ({
|
|
|
7617
7649
|
cwd: workPath,
|
|
7618
7650
|
ignore: predefinedExcludes
|
|
7619
7651
|
};
|
|
7620
|
-
const outputFiles = await (0,
|
|
7621
|
-
outputFiles[`${handlerRbFilename}.rb`] = new
|
|
7652
|
+
const outputFiles = await (0, import_build_utils4.glob)("**", globOptions);
|
|
7653
|
+
outputFiles[`${handlerRbFilename}.rb`] = new import_build_utils4.FileBlob({
|
|
7622
7654
|
data: nowHandlerRbContents
|
|
7623
7655
|
});
|
|
7656
|
+
outputFiles[utilsRbFilename] = new import_build_utils4.FileBlob({
|
|
7657
|
+
data: originalUtilsRbContents
|
|
7658
|
+
});
|
|
7624
7659
|
if (config && (config.includeFiles || config.excludeFiles)) {
|
|
7625
7660
|
const includedPaths = await matchPaths(config.includeFiles, workPath);
|
|
7626
7661
|
const excludedPaths = await matchPaths(
|
|
@@ -7631,7 +7666,7 @@ var build = async ({
|
|
|
7631
7666
|
if (includedPaths.includes(excludedPaths[i])) {
|
|
7632
7667
|
continue;
|
|
7633
7668
|
}
|
|
7634
|
-
if (excludedPaths[i] === `${handlerRbFilename}.rb`) {
|
|
7669
|
+
if (excludedPaths[i] === `${handlerRbFilename}.rb` || excludedPaths[i] === utilsRbFilename) {
|
|
7635
7670
|
continue;
|
|
7636
7671
|
}
|
|
7637
7672
|
if (excludedPaths[i].startsWith(vendorPath)) {
|
|
@@ -7640,18 +7675,29 @@ var build = async ({
|
|
|
7640
7675
|
delete outputFiles[excludedPaths[i]];
|
|
7641
7676
|
}
|
|
7642
7677
|
}
|
|
7643
|
-
const output = new
|
|
7678
|
+
const output = new import_build_utils4.Lambda({
|
|
7644
7679
|
files: outputFiles,
|
|
7645
7680
|
handler: `${handlerRbFilename}.vc__handler`,
|
|
7646
7681
|
runtime,
|
|
7647
7682
|
environment: {}
|
|
7648
7683
|
});
|
|
7684
|
+
try {
|
|
7685
|
+
await (0, import_build_utils2.generateRubyProjectManifest)({
|
|
7686
|
+
workPath,
|
|
7687
|
+
gemfileLockPath: (0, import_path3.join)((0, import_path3.dirname)(gemfilePath), "Gemfile.lock"),
|
|
7688
|
+
framework: config?.framework ?? void 0,
|
|
7689
|
+
serviceType: service ? (0, import_build_utils4.getReportedServiceType)(service) : void 0
|
|
7690
|
+
});
|
|
7691
|
+
} catch (err) {
|
|
7692
|
+
(0, import_build_utils4.debug)(`Failed to write ruby manifest: ${String(err)}`);
|
|
7693
|
+
}
|
|
7649
7694
|
return { output };
|
|
7650
7695
|
};
|
|
7651
7696
|
var shouldServe = () => true;
|
|
7652
7697
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7653
7698
|
0 && (module.exports = {
|
|
7654
7699
|
build,
|
|
7700
|
+
diagnostics,
|
|
7655
7701
|
shouldServe,
|
|
7656
7702
|
startDevServer,
|
|
7657
7703
|
version
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/ruby",
|
|
3
3
|
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index",
|
|
7
7
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
10
|
"vc_init.rb",
|
|
11
|
-
"vc_init_dev.rb"
|
|
11
|
+
"vc_init_dev.rb",
|
|
12
|
+
"vc_utils.rb"
|
|
12
13
|
],
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
@@ -21,15 +22,17 @@
|
|
|
21
22
|
"@types/which": "3.0.0",
|
|
22
23
|
"execa": "2.0.4",
|
|
23
24
|
"fs-extra": "^7.0.1",
|
|
24
|
-
"jest-junit": "16.0.0",
|
|
25
25
|
"semver": "6.3.1",
|
|
26
|
+
"vitest": "2.0.3",
|
|
26
27
|
"which": "3.0.0",
|
|
27
|
-
"@vercel/build-utils": "13.
|
|
28
|
+
"@vercel/build-utils": "13.31.0"
|
|
28
29
|
},
|
|
29
30
|
"scripts": {
|
|
30
31
|
"build": "node ../../utils/build-builder.mjs",
|
|
31
|
-
"test": "
|
|
32
|
+
"test": "vitest run --config ../../vitest.config.mts",
|
|
32
33
|
"test-e2e": "pnpm test",
|
|
33
|
-
"type-check": "tsc --noEmit"
|
|
34
|
+
"type-check": "tsc --noEmit",
|
|
35
|
+
"vitest-run": "vitest -c ../../vitest.config.mts",
|
|
36
|
+
"vitest-e2e": "glob --absolute 'test/**/*.test.js' 'test/**/*.test.ts' 'tests/**/*.test.js' 'tests/**/*.test.ts'"
|
|
34
37
|
}
|
|
35
38
|
}
|
package/vc_init.rb
CHANGED
|
@@ -3,6 +3,7 @@ require 'webrick'
|
|
|
3
3
|
require 'net/http'
|
|
4
4
|
require 'base64'
|
|
5
5
|
require 'json'
|
|
6
|
+
require_relative 'vc__utils__ruby'
|
|
6
7
|
|
|
7
8
|
$entrypoint = '__VC_HANDLER_FILENAME'
|
|
8
9
|
|
|
@@ -10,13 +11,17 @@ ENV['RAILS_ENV'] ||= 'production'
|
|
|
10
11
|
ENV['RACK_ENV'] ||= 'production'
|
|
11
12
|
ENV['RAILS_LOG_TO_STDOUT'] ||= '1'
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
$service_route_prefix = resolve_service_route_prefix
|
|
16
|
+
|
|
17
|
+
def rack_handler(httpMethod, path, body, headers, script_name = '')
|
|
14
18
|
require 'rack'
|
|
15
19
|
|
|
16
20
|
app, _ = Rack::Builder.parse_file($entrypoint)
|
|
17
21
|
server = Rack::MockRequest.new app
|
|
18
22
|
|
|
19
23
|
env = headers.transform_keys { |k| k.split('-').join('_').prepend('HTTP_').upcase }
|
|
24
|
+
env['SCRIPT_NAME'] = script_name unless script_name.empty?
|
|
20
25
|
res = server.request(httpMethod, path, env.merge({ :input => body }))
|
|
21
26
|
|
|
22
27
|
{
|
|
@@ -96,8 +101,13 @@ def vc__handler(event:, context:)
|
|
|
96
101
|
body = Base64.decode64(body)
|
|
97
102
|
end
|
|
98
103
|
|
|
104
|
+
path, script_name = apply_service_route_prefix_to_target(
|
|
105
|
+
path,
|
|
106
|
+
$service_route_prefix
|
|
107
|
+
)
|
|
108
|
+
|
|
99
109
|
if $entrypoint.end_with? '.ru'
|
|
100
|
-
return rack_handler(httpMethod, path, body, headers)
|
|
110
|
+
return rack_handler(httpMethod, path, body, headers, script_name)
|
|
101
111
|
end
|
|
102
112
|
|
|
103
113
|
return webrick_handler(httpMethod, path, body, headers)
|
package/vc_init_dev.rb
CHANGED
|
@@ -10,12 +10,14 @@ require 'rack'
|
|
|
10
10
|
require 'rack/handler/webrick'
|
|
11
11
|
require 'webrick'
|
|
12
12
|
require 'socket'
|
|
13
|
+
require_relative 'vc__utils__ruby'
|
|
13
14
|
|
|
14
15
|
$stdout.sync = true
|
|
15
16
|
$stderr.sync = true
|
|
16
17
|
|
|
17
18
|
USER_ENTRYPOINT = "__VC_DEV_ENTRYPOINT__"
|
|
18
19
|
PUBLIC_DIR = 'public'
|
|
20
|
+
SERVICE_ROUTE_PREFIX = resolve_service_route_prefix
|
|
19
21
|
|
|
20
22
|
def build_user_app
|
|
21
23
|
if USER_ENTRYPOINT.end_with?('.ru')
|
|
@@ -36,17 +38,33 @@ class StaticThenApp
|
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
def call(env)
|
|
39
|
-
|
|
41
|
+
effective_env = env.dup
|
|
42
|
+
req_path, matched_prefix = strip_service_route_prefix(
|
|
43
|
+
effective_env['PATH_INFO'] || '/',
|
|
44
|
+
SERVICE_ROUTE_PREFIX
|
|
45
|
+
)
|
|
46
|
+
effective_env['PATH_INFO'] = req_path
|
|
47
|
+
|
|
48
|
+
unless matched_prefix.empty?
|
|
49
|
+
script_name = effective_env['SCRIPT_NAME'] || ''
|
|
50
|
+
if !script_name.empty? && script_name != '/'
|
|
51
|
+
script_name = script_name.sub(%r{/+\z}, '')
|
|
52
|
+
else
|
|
53
|
+
script_name = ''
|
|
54
|
+
end
|
|
55
|
+
effective_env['SCRIPT_NAME'] = "#{script_name}#{matched_prefix}"
|
|
56
|
+
end
|
|
57
|
+
|
|
40
58
|
# Normalize path and guard against traversal
|
|
41
59
|
safe = req_path.sub(/^\//, '')
|
|
42
60
|
full = File.expand_path(safe, @base)
|
|
43
61
|
|
|
44
62
|
if full.start_with?(@base + File::SEPARATOR) && File.file?(full)
|
|
45
63
|
# Delegate to Rack::File which handles HEAD/GET correctly
|
|
46
|
-
return @file_server.call(
|
|
64
|
+
return @file_server.call(effective_env)
|
|
47
65
|
end
|
|
48
66
|
|
|
49
|
-
@app.call(
|
|
67
|
+
@app.call(effective_env)
|
|
50
68
|
end
|
|
51
69
|
end
|
|
52
70
|
|
package/vc_utils.rb
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
3
|
+
# Normalize a configured service prefix into canonical mount-path form.
|
|
4
|
+
#
|
|
5
|
+
# Returns an empty string when unset/blank/root ("/"), otherwise:
|
|
6
|
+
# - always starts with "/"
|
|
7
|
+
# - has no trailing slash
|
|
8
|
+
def normalize_service_route_prefix(raw_prefix)
|
|
9
|
+
return '' if raw_prefix.nil?
|
|
10
|
+
|
|
11
|
+
prefix = raw_prefix.strip
|
|
12
|
+
return '' if prefix.empty?
|
|
13
|
+
|
|
14
|
+
prefix = "/#{prefix}" unless prefix.start_with?('/')
|
|
15
|
+
|
|
16
|
+
if prefix != '/'
|
|
17
|
+
prefix = prefix.sub(%r{/+\z}, '')
|
|
18
|
+
prefix = '/' if prefix.empty?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
prefix == '/' ? '' : prefix
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Return whether service-prefix stripping is explicitly enabled.
|
|
25
|
+
#
|
|
26
|
+
# Stripping is gated behind a dedicated env var so manually configured
|
|
27
|
+
# route prefixes can be preserved by default.
|
|
28
|
+
def service_route_prefix_strip_enabled?
|
|
29
|
+
raw = ENV['VERCEL_SERVICE_ROUTE_PREFIX_STRIP']
|
|
30
|
+
return false if raw.nil? || raw.empty?
|
|
31
|
+
|
|
32
|
+
%w[1 true].include?(raw.downcase)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def resolve_service_route_prefix
|
|
36
|
+
return '' unless service_route_prefix_strip_enabled?
|
|
37
|
+
|
|
38
|
+
normalize_service_route_prefix(ENV['VERCEL_SERVICE_ROUTE_PREFIX'])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Split an HTTP request-target into [path, query].
|
|
42
|
+
#
|
|
43
|
+
# Supports:
|
|
44
|
+
# - origin-form: "/a/b?x=1"
|
|
45
|
+
# - absolute-form: "https://example.com/a/b?x=1"
|
|
46
|
+
# - asterisk-form: "*"
|
|
47
|
+
def split_request_target(target)
|
|
48
|
+
return ['/', ''] if target.nil? || target.empty?
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
parsed = URI.parse(target)
|
|
52
|
+
if parsed.scheme && parsed.host
|
|
53
|
+
path = parsed.path.nil? || parsed.path.empty? ? '/' : parsed.path
|
|
54
|
+
return [path, parsed.query.to_s]
|
|
55
|
+
end
|
|
56
|
+
rescue URI::InvalidURIError
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
return ['*', ''] if target == '*'
|
|
60
|
+
|
|
61
|
+
path, query = target.split('?', 2)
|
|
62
|
+
path = '/' if path.nil? || path.empty?
|
|
63
|
+
path = "/#{path}" unless path.start_with?('/')
|
|
64
|
+
[path, query.to_s]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Strip the configured service route prefix from a request path.
|
|
68
|
+
#
|
|
69
|
+
# Returns:
|
|
70
|
+
# - stripped path passed to the user app
|
|
71
|
+
# - matched mount prefix (empty when no prefix matched)
|
|
72
|
+
def strip_service_route_prefix(path, prefix)
|
|
73
|
+
return [path, ''] if path == '*'
|
|
74
|
+
|
|
75
|
+
normalized_path = path.nil? || path.empty? ? '/' : path
|
|
76
|
+
normalized_path = "/#{normalized_path}" unless normalized_path.start_with?('/')
|
|
77
|
+
|
|
78
|
+
normalized_prefix = prefix.to_s
|
|
79
|
+
return [normalized_path, ''] if normalized_prefix.empty?
|
|
80
|
+
|
|
81
|
+
return ['/', normalized_prefix] if normalized_path == normalized_prefix
|
|
82
|
+
|
|
83
|
+
if normalized_path.start_with?("#{normalized_prefix}/")
|
|
84
|
+
stripped = normalized_path[normalized_prefix.length..]
|
|
85
|
+
return [stripped.nil? || stripped.empty? ? '/' : stripped, normalized_prefix]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
[normalized_path, '']
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Apply service-prefix stripping to a full request-target.
|
|
92
|
+
#
|
|
93
|
+
# Returns:
|
|
94
|
+
# - updated request-target (path + optional query)
|
|
95
|
+
# - matched mount prefix for Rack SCRIPT_NAME (or "")
|
|
96
|
+
def apply_service_route_prefix_to_target(target, prefix)
|
|
97
|
+
path, query = split_request_target(target)
|
|
98
|
+
path, script_name = strip_service_route_prefix(path, prefix)
|
|
99
|
+
updated = query.empty? ? path : "#{path}?#{query}"
|
|
100
|
+
[updated, script_name]
|
|
101
|
+
end
|