mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
❇️
This commit is contained in:
18014
examples/next/package-lock.json
generated
18014
examples/next/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bun-examples/next",
|
||||
"version": "0.0.31",
|
||||
"version": "0.0.32",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"next": "11.1.2",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url("./2.css");
|
||||
|
||||
.container {
|
||||
min-height: 99vh;
|
||||
padding: 0 0.5rem;
|
||||
|
||||
@@ -14,8 +14,3 @@ a {
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: url(https://wompampsupport.azureedge.net/fetchimage?siteId=7575&v=2&jpgQuality=100&width=700&url=https%3A%2F%2Fi.kym-cdn.com%2Fentries%2Ficons%2Foriginal%2F000%2F013%2F564%2Fdoge.jpg);
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bun-examples/react",
|
||||
"version": "0.0.27",
|
||||
"version": "0.0.28",
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
|
||||
@@ -71,5 +71,12 @@ pub fn main() anyerror!void {
|
||||
tarball_buf_list = std.ArrayListUnmanaged(u8){ .capacity = file_buf.len, .items = file_buf };
|
||||
}
|
||||
|
||||
try Archive.extractToDisk(file_buf, folder, null, 1, false);
|
||||
try Archive.extractToDisk(
|
||||
file_buf,
|
||||
folder,
|
||||
null,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ const HelpCommand = struct {
|
||||
const fmt =
|
||||
\\> <r> <b><green>dev <r><d> ./a.ts ./b.jsx<r> Start a Bun Dev Server
|
||||
\\> <r> <b><magenta>bun <r><d> ./a.ts ./b.jsx<r> Bundle dependencies of input files into a <r><magenta>.bun<r>
|
||||
\\> <r> <b><cyan>create <r><d> next<r> <r> Start a new project from a template <d>(shorthand: c)<r>
|
||||
\\> <r> <b><cyan>create <r><d> next ./app<r> Start a new project from a template <d>(shorthand: c)<r>
|
||||
\\> <r> <b><blue>discord <r> Open Bun's Discord server
|
||||
\\> <r> <b><d>help <r> Print this help menu
|
||||
\\
|
||||
@@ -519,7 +519,7 @@ pub const Command = struct {
|
||||
|
||||
pub fn create(allocator: *std.mem.Allocator, log: *logger.Log, comptime command: Command.Tag) anyerror!Context {
|
||||
return Command.Context{
|
||||
.args = if (comptime command != Command.Tag.CreateCommand)
|
||||
.args = if (comptime command != Command.Tag.CreateCommand)
|
||||
try Arguments.parse(allocator, command)
|
||||
else
|
||||
std.mem.zeroes(Api.TransformOptions),
|
||||
|
||||
@@ -41,6 +41,17 @@ const skip_files = &[_]string{
|
||||
"pnpm-lock.yaml",
|
||||
};
|
||||
|
||||
const never_conflict = &[_]string{
|
||||
"README.md",
|
||||
"gitignore",
|
||||
".gitignore",
|
||||
".git/",
|
||||
};
|
||||
|
||||
const npm_task_args = &[_]string{
|
||||
"exec",
|
||||
};
|
||||
|
||||
var bun_path: ?[:0]const u8 = null;
|
||||
fn execTask(allocator: *std.mem.Allocator, task_: string, cwd: string, PATH: string, npm_client: NPMClient) void {
|
||||
const task = std.mem.trim(u8, task_, " \n\r\t");
|
||||
@@ -52,13 +63,17 @@ fn execTask(allocator: *std.mem.Allocator, task_: string, cwd: string, PATH: str
|
||||
count += 1;
|
||||
}
|
||||
|
||||
var argv = allocator.alloc(string, count + 2) catch return;
|
||||
const npm_args = 2;
|
||||
const total = count + npm_args;
|
||||
var argv = allocator.alloc(string, total) catch return;
|
||||
defer allocator.free(argv);
|
||||
|
||||
argv[0] = npm_client.bin;
|
||||
argv[1] = "exec";
|
||||
argv[1] = npm_task_args[0];
|
||||
|
||||
{
|
||||
var i: usize = 2;
|
||||
|
||||
splitter = std.mem.split(u8, task, " ");
|
||||
while (splitter.next()) |split| {
|
||||
argv[i] = split;
|
||||
@@ -67,9 +82,10 @@ fn execTask(allocator: *std.mem.Allocator, task_: string, cwd: string, PATH: str
|
||||
}
|
||||
|
||||
if (strings.startsWith(task, "bun ")) {
|
||||
// TODO: use self exe
|
||||
if (bun_path orelse which(&bun_path_buf, PATH, cwd, "bun")) |bun_path_| {
|
||||
bun_path = bun_path_;
|
||||
argv = argv[2..];
|
||||
argv = argv[npm_args..];
|
||||
argv[0] = std.mem.span(bun_path_);
|
||||
}
|
||||
}
|
||||
@@ -98,6 +114,23 @@ fn execTask(allocator: *std.mem.Allocator, task_: string, cwd: string, PATH: str
|
||||
_ = proc.spawnAndWait() catch undefined;
|
||||
}
|
||||
|
||||
// We don't want to allocate memory each time
|
||||
// But we cannot print over an existing buffer or weird stuff will happen
|
||||
// so we keep two and switch between them
|
||||
pub const ProgressBuf = struct {
|
||||
var bufs: [2][1024]u8 = [2][1024]u8{
|
||||
@as([1024]u8, undefined),
|
||||
@as([1024]u8, undefined),
|
||||
};
|
||||
|
||||
var buf_index: usize = 0;
|
||||
|
||||
pub fn print(comptime fmt: string, args: anytype) !string {
|
||||
buf_index += 1;
|
||||
return try std.fmt.bufPrint(std.mem.span(&bufs[buf_index % 2]), fmt, args);
|
||||
}
|
||||
};
|
||||
|
||||
const CreateOptions = struct {
|
||||
npm_client: ?NPMClient.Tag = null,
|
||||
skip_install: bool = false,
|
||||
@@ -131,6 +164,7 @@ const CreateOptions = struct {
|
||||
}
|
||||
|
||||
Output.prettyln("<r><b>bun create<r> flags:\n", .{});
|
||||
Output.flush();
|
||||
clap.help(Output.writer(), params[1..]) catch {};
|
||||
Output.flush();
|
||||
std.os.exit(0);
|
||||
@@ -169,7 +203,7 @@ const BUN_CREATE_DIR = ".bun-create";
|
||||
var home_dir_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
pub const CreateCommand = struct {
|
||||
var client: HTTPClient = undefined;
|
||||
var extracting_name_buf: [1024]u8 = undefined;
|
||||
|
||||
pub fn exec(ctx: Command.Context, positionals: []const []const u8) !void {
|
||||
var create_options = try CreateOptions.parse(ctx.allocator, false);
|
||||
|
||||
@@ -226,10 +260,8 @@ pub const CreateCommand = struct {
|
||||
const destination = try filesystem.dirname_store.append([]const u8, resolve_path.joinAbs(filesystem.top_level_dir, .auto, dirname));
|
||||
|
||||
var progress = std.Progress{};
|
||||
|
||||
var node_ = try progress.start(try std.fmt.bufPrint(&extracting_name_buf, "Loading {s}", .{template}), 0);
|
||||
var node = try progress.start(try ProgressBuf.print("Loading {s}", .{template}), 0);
|
||||
progress.supports_ansi_escape_codes = Output.enable_ansi_colors;
|
||||
var node = node_.start("Downloading", 0);
|
||||
|
||||
// alacritty is fast
|
||||
if (env_loader.map.get("ALACRITTY_LOG") != null) {
|
||||
@@ -237,7 +269,6 @@ pub const CreateCommand = struct {
|
||||
}
|
||||
|
||||
defer {
|
||||
progress.root.end();
|
||||
progress.refresh();
|
||||
}
|
||||
|
||||
@@ -247,14 +278,31 @@ pub const CreateCommand = struct {
|
||||
const is_remote_template = !std.fs.path.isAbsolute(template);
|
||||
|
||||
if (is_remote_template) {
|
||||
var tarball_bytes: MutableString = try Example.fetch(ctx, template, &progress, &node);
|
||||
var tarball_bytes: MutableString = Example.fetch(ctx, template, &progress, node) catch |err| {
|
||||
switch (err) {
|
||||
error.HTTPForbidden, error.ExampleNotFound => {
|
||||
node.end();
|
||||
progress.refresh();
|
||||
|
||||
node.end();
|
||||
Output.prettyError("\n<r><red>error:<r> <b>\"{s}\"<r> was not found. Here are templates you can use:\n\n", .{
|
||||
template,
|
||||
});
|
||||
Output.flush();
|
||||
const examples = try Example.fetchAllLocalAndRemote(ctx, &env_loader, filesystem);
|
||||
Example.print(examples.items, dirname);
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
},
|
||||
else => {
|
||||
return err;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
node = progress.root.start(try std.fmt.bufPrint(&extracting_name_buf, "Decompressing {s}", .{template}), 0);
|
||||
node.name = try ProgressBuf.print("Decompressing {s}", .{template});
|
||||
node.setCompletedItems(0);
|
||||
node.setEstimatedTotalItems(0);
|
||||
node.activate();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
var file_buf = try ctx.allocator.alloc(u8, 16384);
|
||||
@@ -264,12 +312,10 @@ pub const CreateCommand = struct {
|
||||
try gunzip.readAll();
|
||||
gunzip.deinit();
|
||||
|
||||
node.end();
|
||||
|
||||
node = progress.root.start(try std.fmt.bufPrint(&extracting_name_buf, "Extracting {s}", .{template}), 0);
|
||||
node.name = try ProgressBuf.print("Extracting {s}", .{template});
|
||||
node.setCompletedItems(0);
|
||||
node.setEstimatedTotalItems(0);
|
||||
node.activate();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
var pluckers = [_]Archive.Plucker{
|
||||
@@ -292,9 +338,12 @@ pub const CreateCommand = struct {
|
||||
1,
|
||||
);
|
||||
|
||||
inline for (never_conflict) |never_conflict_path| {
|
||||
_ = archive_context.overwrite_list.swapRemove(never_conflict_path);
|
||||
}
|
||||
|
||||
if (archive_context.overwrite_list.count() > 0) {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
progress.refresh();
|
||||
|
||||
// Thank you create-react-app for this copy (and idea)
|
||||
@@ -322,31 +371,32 @@ pub const CreateCommand = struct {
|
||||
&archive_context,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
var plucker = pluckers[0];
|
||||
|
||||
if (!plucker.found or plucker.fd == 0) {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
Output.prettyErrorln("package.json not found. This package is corrupt. Please try again or file an issue if it keeps happening.", .{});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
node.end();
|
||||
node = progress.root.start(try std.fmt.bufPrint(&extracting_name_buf, "Updating package.json", .{}), 0);
|
||||
|
||||
node.activate();
|
||||
node.name = "Updating package.json";
|
||||
progress.refresh();
|
||||
|
||||
package_json_contents = plucker.contents;
|
||||
package_json_file = std.fs.File{ .handle = plucker.fd };
|
||||
} else {
|
||||
var template_parts = [_]string{template};
|
||||
|
||||
node.name = "Copying files";
|
||||
progress.refresh();
|
||||
|
||||
const template_dir = std.fs.openDirAbsolute(filesystem.abs(&template_parts), .{ .iterate = true }) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("<r><red>{s}<r>: opening dir {s}", .{ @errorName(err), template });
|
||||
@@ -357,7 +407,7 @@ pub const CreateCommand = struct {
|
||||
std.fs.deleteTreeAbsolute(destination) catch {};
|
||||
const destination_dir = std.fs.cwd().makeOpenPath(destination, .{ .iterate = true }) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("<r><red>{s}<r>: creating dir {s}", .{ @errorName(err), destination });
|
||||
@@ -369,6 +419,8 @@ pub const CreateCommand = struct {
|
||||
var walker = try Walker.walk(template_dir, ctx.allocator, skip_files, skip_dirs);
|
||||
defer walker.deinit();
|
||||
|
||||
var count: usize = 0;
|
||||
|
||||
while (try walker.next()) |entry| {
|
||||
// TODO: make this not walk these folders entirely
|
||||
// rather than checking each file path.....
|
||||
@@ -379,7 +431,7 @@ pub const CreateCommand = struct {
|
||||
}
|
||||
break :brk destination_dir.createFile(entry.path, .{}) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path });
|
||||
@@ -388,13 +440,14 @@ pub const CreateCommand = struct {
|
||||
};
|
||||
};
|
||||
defer outfile.close();
|
||||
defer node.completeOne();
|
||||
|
||||
var infile = try entry.dir.openFile(entry.basename, .{ .read = true });
|
||||
defer infile.close();
|
||||
CopyFile.copy(infile.handle, outfile.handle) catch {
|
||||
entry.dir.copyFile(entry.basename, destination_dir, entry.path, .{}) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path });
|
||||
@@ -408,7 +461,7 @@ pub const CreateCommand = struct {
|
||||
|
||||
package_json_file = destination_dir.openFile("package.json", .{ .read = true, .write = true }) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("Failed to open package.json due to error <r><red>{s}", .{@errorName(err)});
|
||||
@@ -417,7 +470,7 @@ pub const CreateCommand = struct {
|
||||
};
|
||||
const stat = package_json_file.stat() catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("Failed to stat package.json due to error <r><red>{s}", .{@errorName(err)});
|
||||
@@ -427,7 +480,7 @@ pub const CreateCommand = struct {
|
||||
|
||||
if (stat.kind != .File or stat.size == 0) {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("package.json must be a file with content", .{});
|
||||
@@ -439,7 +492,7 @@ pub const CreateCommand = struct {
|
||||
|
||||
_ = package_json_file.preadAll(package_json_contents.list.items, 0) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("Error reading package.json: <r><red>{s}", .{@errorName(err)});
|
||||
@@ -453,22 +506,17 @@ pub const CreateCommand = struct {
|
||||
js_ast.Stmt.Data.Store.create(default_allocator);
|
||||
}
|
||||
|
||||
node.end();
|
||||
progress.refresh();
|
||||
|
||||
var source = logger.Source.initPathString("package.json", package_json_contents.list.items);
|
||||
var package_json_expr = ParseJSON(&source, ctx.log, ctx.allocator) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("package.json failed to parse with error: {s}", .{@errorName(err)});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
};
|
||||
|
||||
if (ctx.log.errors > 0) {
|
||||
node.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
if (Output.enable_ansi_colors) {
|
||||
try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true);
|
||||
} else {
|
||||
@@ -481,11 +529,6 @@ pub const CreateCommand = struct {
|
||||
|
||||
if (package_json_expr.asProperty("name")) |name_expr| {
|
||||
if (name_expr.expr.data != .e_string) {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("package.json failed to parse correctly. its missing a name. it shouldnt be missing a name.", .{});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
@@ -494,11 +537,6 @@ pub const CreateCommand = struct {
|
||||
var basename = std.fs.path.basename(destination);
|
||||
name_expr.expr.data.e_string.utf8 = @intToPtr([*]u8, @ptrToInt(basename.ptr))[0..basename.len];
|
||||
} else {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("package.json failed to parse correctly. its missing a name. it shouldnt be missing a name.", .{});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
@@ -569,9 +607,6 @@ pub const CreateCommand = struct {
|
||||
}
|
||||
}
|
||||
|
||||
node.name = "Saving package.json";
|
||||
progress.maybeRefresh();
|
||||
|
||||
var package_json_writer = JSPrinter.NewFileWriter(package_json_file);
|
||||
|
||||
_ = JSPrinter.printJSON(@TypeOf(package_json_writer), package_json_writer, package_json_expr, &source) catch |err| {
|
||||
@@ -591,10 +626,10 @@ pub const CreateCommand = struct {
|
||||
var realpath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
if (create_options.npm_client) |tag| {
|
||||
if (which(&realpath_buf, PATH, filesystem.top_level_dir, @tagName(tag))) |bin| {
|
||||
if (which(&realpath_buf, PATH, destination, @tagName(tag))) |bin| {
|
||||
npm_client_ = NPMClient{ .tag = tag, .bin = try ctx.allocator.dupe(u8, bin) };
|
||||
}
|
||||
} else if (try NPMClient.detect(ctx.allocator, &realpath_buf, PATH, filesystem.top_level_dir, true)) |npmclient| {
|
||||
} else if (try NPMClient.detect(ctx.allocator, &realpath_buf, PATH, destination, true)) |npmclient| {
|
||||
npm_client_ = NPMClient{
|
||||
.bin = try ctx.allocator.dupe(u8, npmclient.bin),
|
||||
.tag = npmclient.tag,
|
||||
@@ -604,31 +639,35 @@ pub const CreateCommand = struct {
|
||||
}
|
||||
|
||||
if (npm_client_ != null and preinstall_tasks.items.len > 0) {
|
||||
node.end();
|
||||
node = progress.root.start("Running pre-install tasks", preinstall_tasks.items.len);
|
||||
node.setCompletedItems(0);
|
||||
progress.refresh();
|
||||
|
||||
for (preinstall_tasks.items) |task, i| {
|
||||
execTask(ctx.allocator, task, destination, PATH, npm_client_.?);
|
||||
|
||||
node.setCompletedItems(i);
|
||||
progress.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
node.end();
|
||||
|
||||
if (npm_client_) |npm_client| {
|
||||
const start_time = std.time.nanoTimestamp();
|
||||
var install_args = [_]string{ npm_client.bin, "install" };
|
||||
Output.printError("\n", .{});
|
||||
const install_args_ = [_]string{ npm_client.bin, "install", "--loglevel=error", "--no-fund", "--no-audit" };
|
||||
const len: usize = switch (npm_client.tag) {
|
||||
.npm => install_args_.len,
|
||||
else => 2,
|
||||
};
|
||||
|
||||
const install_args = install_args_[0..len];
|
||||
Output.flush();
|
||||
Output.pretty("\n<r><d>$ <b><cyan>{s}<r><d> install", .{@tagName(npm_client.tag)});
|
||||
var writer = Output.writer();
|
||||
|
||||
if (install_args.len > 2) {
|
||||
for (install_args[2..]) |arg| {
|
||||
Output.pretty(" ", .{});
|
||||
Output.pretty("{s}", .{arg});
|
||||
}
|
||||
}
|
||||
|
||||
Output.pretty("<r>\n", .{});
|
||||
Output.flush();
|
||||
|
||||
Output.prettyln("\n<r><d>$ <b><cyan>{s}<r><d> install<r>", .{@tagName(npm_client.tag)});
|
||||
Output.flush();
|
||||
|
||||
var process = try std.ChildProcess.init(&install_args, ctx.allocator);
|
||||
var process = try std.ChildProcess.init(install_args, ctx.allocator);
|
||||
process.cwd = destination;
|
||||
|
||||
defer {
|
||||
@@ -649,30 +688,22 @@ pub const CreateCommand = struct {
|
||||
progress.log("Failed to detect npm client. Tried pnpm, yarn, and npm.\n", .{});
|
||||
}
|
||||
|
||||
progress.refresh();
|
||||
|
||||
if (npm_client_ != null and !create_options.skip_install and postinstall_tasks.items.len > 0) {
|
||||
node.end();
|
||||
node = progress.root.start("Running post-install tasks", postinstall_tasks.items.len);
|
||||
node.setCompletedItems(0);
|
||||
progress.refresh();
|
||||
|
||||
for (postinstall_tasks.items) |task, i| {
|
||||
execTask(ctx.allocator, task, destination, PATH, npm_client_.?);
|
||||
|
||||
node.setCompletedItems(i);
|
||||
progress.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
var parent_dir = try std.fs.openDirAbsolute(destination, .{});
|
||||
std.os.linkat(parent_dir.fd, "gitignore", parent_dir.fd, ".gitignore", 0) catch {};
|
||||
std.os.unlinkat(
|
||||
parent_dir.fd,
|
||||
"gitignore",
|
||||
0,
|
||||
) catch {};
|
||||
parent_dir.close();
|
||||
{
|
||||
var parent_dir = try std.fs.openDirAbsolute(destination, .{});
|
||||
std.os.linkat(parent_dir.fd, "gitignore", parent_dir.fd, ".gitignore", 0) catch {};
|
||||
std.os.unlinkat(
|
||||
parent_dir.fd,
|
||||
"gitignore",
|
||||
0,
|
||||
) catch {};
|
||||
parent_dir.close();
|
||||
}
|
||||
|
||||
if (!create_options.skip_git) {
|
||||
if (which(&bun_path_buf, PATH, destination, "git")) |git| {
|
||||
@@ -702,6 +733,48 @@ pub const CreateCommand = struct {
|
||||
Output.printStartEnd(ctx.start_time, std.time.nanoTimestamp());
|
||||
Output.prettyErrorln(" <r><d>bun create {s}<r>", .{template});
|
||||
Output.flush();
|
||||
|
||||
Output.pretty(
|
||||
\\
|
||||
\\<r><d>-----<r>
|
||||
\\
|
||||
, .{});
|
||||
|
||||
if (!create_options.skip_git and !create_options.skip_install) {
|
||||
Output.pretty(
|
||||
\\
|
||||
\\<d>A local git repository was created for you and dependencies were installed automatically.<r>
|
||||
\\
|
||||
, .{});
|
||||
} else if (!create_options.skip_git) {
|
||||
Output.pretty(
|
||||
\\
|
||||
\\<d>A local git repository was created for you.<r>
|
||||
\\
|
||||
, .{});
|
||||
} else if (!create_options.skip_install) {
|
||||
Output.pretty(
|
||||
\\
|
||||
\\<d>Dependencies were installed automatically.<r>
|
||||
\\
|
||||
, .{});
|
||||
}
|
||||
|
||||
Output.pretty(
|
||||
\\
|
||||
\\<b>Created <green>{s}<r> project successfully
|
||||
\\
|
||||
\\<d>#<r><b> To get started, run:<r>
|
||||
\\
|
||||
\\ <b><cyan>cd {s}<r>
|
||||
\\ <b><cyan>bun<r>
|
||||
\\
|
||||
, .{
|
||||
std.fs.path.basename(template),
|
||||
filesystem.relativeTo(destination),
|
||||
});
|
||||
|
||||
Output.flush();
|
||||
}
|
||||
};
|
||||
const Commands = .{
|
||||
@@ -760,18 +833,19 @@ pub const Example = struct {
|
||||
var url: URL = undefined;
|
||||
pub const timeout: u32 = 6000;
|
||||
|
||||
pub fn print(examples: []const Example) void {
|
||||
var app_name_buf: [512]u8 = undefined;
|
||||
pub fn print(examples: []const Example, default_app_name: ?string) void {
|
||||
for (examples) |example, i| {
|
||||
var app_name = example.name;
|
||||
var app_name = default_app_name orelse (std.fmt.bufPrint(&app_name_buf, "./{s}-app", .{example.name[0..std.math.min(example.name.len, 492)]}) catch unreachable);
|
||||
|
||||
if (example.description.len > 0) {
|
||||
Output.pretty(" <r># {s}<r>\n <b>bun create <cyan>{s}<r><b> ./{s}-app<r>\n<d> \n\n", .{
|
||||
Output.pretty(" <r># {s}<r>\n <b>bun create <cyan>{s}<r><b> {s}<r>\n<d> \n\n", .{
|
||||
example.description,
|
||||
example.name,
|
||||
app_name,
|
||||
});
|
||||
} else {
|
||||
Output.pretty(" <r><b>bun create <cyan>{s}<r><b> ./{s}-app<r>\n\n", .{
|
||||
Output.pretty(" <r><b>bun create <cyan>{s}<r><b> {s}<r>\n\n", .{
|
||||
example.name,
|
||||
app_name,
|
||||
});
|
||||
@@ -779,31 +853,76 @@ pub const Example = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetchFromDisk(ctx: Command.Context, absolute_path: string, refresher: *std.Progress, progress: *std.Progress.Node) !MutableString {
|
||||
progress.name = "Reading local package";
|
||||
refresher.refresh();
|
||||
pub fn fetchAllLocalAndRemote(ctx: Command.Context, env_loader: *DotEnv.Loader, filesystem: *fs.FileSystem) !std.ArrayList(Example) {
|
||||
const remote_examples = try Example.fetchAll(ctx);
|
||||
|
||||
var package = try std.fs.openFileAbsolute(absolute_path, .{ .read = true });
|
||||
var stat = try package.stat();
|
||||
if (stat.kind != .File) {
|
||||
progress.end();
|
||||
Output.prettyErrorln("<r>{s} is not a file", .{absolute_path});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
var examples = std.ArrayList(Example).fromOwnedSlice(ctx.allocator, remote_examples);
|
||||
{
|
||||
var folders = [3]std.fs.Dir{ std.fs.Dir{ .fd = 0 }, std.fs.Dir{ .fd = 0 }, std.fs.Dir{ .fd = 0 } };
|
||||
if (env_loader.map.get("BUN_CREATE_DIR")) |home_dir| {
|
||||
var parts = [_]string{home_dir};
|
||||
var outdir_path = filesystem.absBuf(&parts, &home_dir_buf);
|
||||
folders[0] = std.fs.openDirAbsolute(outdir_path, .{ .iterate = true }) catch std.fs.Dir{ .fd = 0 };
|
||||
}
|
||||
|
||||
{
|
||||
var parts = [_]string{ filesystem.top_level_dir, BUN_CREATE_DIR };
|
||||
var outdir_path = filesystem.absBuf(&parts, &home_dir_buf);
|
||||
folders[1] = std.fs.openDirAbsolute(outdir_path, .{ .iterate = true }) catch std.fs.Dir{ .fd = 0 };
|
||||
}
|
||||
|
||||
if (env_loader.map.get("HOME")) |home_dir| {
|
||||
var parts = [_]string{ home_dir, BUN_CREATE_DIR };
|
||||
var outdir_path = filesystem.absBuf(&parts, &home_dir_buf);
|
||||
folders[2] = std.fs.openDirAbsolute(outdir_path, .{ .iterate = true }) catch std.fs.Dir{ .fd = 0 };
|
||||
}
|
||||
|
||||
// subfolders with package.json
|
||||
for (folders) |folder_| {
|
||||
if (folder_.fd != 0) {
|
||||
const folder: std.fs.Dir = folder_;
|
||||
var iter = folder.iterate();
|
||||
|
||||
loop: while (iter.next() catch null) |entry_| {
|
||||
const entry: std.fs.Dir.Entry = entry_;
|
||||
|
||||
switch (entry.kind) {
|
||||
.Directory => {
|
||||
inline for (skip_dirs) |skip_dir| {
|
||||
if (strings.eqlComptime(entry.name, skip_dir)) {
|
||||
continue :loop;
|
||||
}
|
||||
}
|
||||
|
||||
std.mem.copy(u8, &home_dir_buf, entry.name);
|
||||
home_dir_buf[entry.name.len] = std.fs.path.sep;
|
||||
std.mem.copy(u8, home_dir_buf[entry.name.len + 1 ..], "package.json");
|
||||
home_dir_buf[entry.name.len + 1 + "package.json".len] = 0;
|
||||
|
||||
var path: [:0]u8 = home_dir_buf[0 .. entry.name.len + 1 + "package.json".len :0];
|
||||
|
||||
folder.accessZ(path, .{
|
||||
.read = true,
|
||||
}) catch continue :loop;
|
||||
|
||||
try examples.append(
|
||||
Example{
|
||||
.name = try filesystem.filename_store.append(@TypeOf(entry.name), entry.name),
|
||||
.version = "",
|
||||
.local = true,
|
||||
.description = "",
|
||||
},
|
||||
);
|
||||
continue :loop;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stat.size == 0) {
|
||||
progress.end();
|
||||
Output.prettyErrorln("<r>{s} is an empty file", .{absolute_path});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
var mutable_string = try MutableString.init(ctx.allocator, stat.size);
|
||||
mutable_string.list.expandToCapacity();
|
||||
var bytes = try package.readAll(mutable_string.list.items);
|
||||
try mutable_string.inflate(bytes);
|
||||
return mutable_string;
|
||||
return examples;
|
||||
}
|
||||
|
||||
pub fn fetch(ctx: Command.Context, name: string, refresher: *std.Progress, progress: *std.Progress.Node) !MutableString {
|
||||
@@ -1020,79 +1139,13 @@ pub const CreateListExamplesCommand = struct {
|
||||
env_loader.loadProcess();
|
||||
|
||||
const time = std.time.nanoTimestamp();
|
||||
const remote_examples = try Example.fetchAll(ctx);
|
||||
|
||||
var examples = std.ArrayList(Example).fromOwnedSlice(ctx.allocator, remote_examples);
|
||||
{
|
||||
var folders = [3]std.fs.Dir{ std.fs.Dir{ .fd = 0 }, std.fs.Dir{ .fd = 0 }, std.fs.Dir{ .fd = 0 } };
|
||||
if (env_loader.map.get("BUN_CREATE_DIR")) |home_dir| {
|
||||
var parts = [_]string{home_dir};
|
||||
var outdir_path = filesystem.absBuf(&parts, &home_dir_buf);
|
||||
folders[0] = std.fs.openDirAbsolute(outdir_path, .{ .iterate = true }) catch std.fs.Dir{ .fd = 0 };
|
||||
}
|
||||
|
||||
{
|
||||
var parts = [_]string{ filesystem.top_level_dir, BUN_CREATE_DIR };
|
||||
var outdir_path = filesystem.absBuf(&parts, &home_dir_buf);
|
||||
folders[1] = std.fs.openDirAbsolute(outdir_path, .{ .iterate = true }) catch std.fs.Dir{ .fd = 0 };
|
||||
}
|
||||
|
||||
if (env_loader.map.get("HOME")) |home_dir| {
|
||||
var parts = [_]string{ home_dir, BUN_CREATE_DIR };
|
||||
var outdir_path = filesystem.absBuf(&parts, &home_dir_buf);
|
||||
folders[2] = std.fs.openDirAbsolute(outdir_path, .{ .iterate = true }) catch std.fs.Dir{ .fd = 0 };
|
||||
}
|
||||
|
||||
// subfolders with package.json
|
||||
for (folders) |folder_| {
|
||||
if (folder_.fd != 0) {
|
||||
const folder: std.fs.Dir = folder_;
|
||||
var iter = folder.iterate();
|
||||
|
||||
loop: while (iter.next() catch null) |entry_| {
|
||||
const entry: std.fs.Dir.Entry = entry_;
|
||||
|
||||
switch (entry.kind) {
|
||||
.Directory => {
|
||||
inline for (skip_dirs) |skip_dir| {
|
||||
if (strings.eqlComptime(entry.name, skip_dir)) {
|
||||
continue :loop;
|
||||
}
|
||||
}
|
||||
|
||||
std.mem.copy(u8, &home_dir_buf, entry.name);
|
||||
home_dir_buf[entry.name.len] = std.fs.path.sep;
|
||||
std.mem.copy(u8, home_dir_buf[entry.name.len + 1 ..], "package.json");
|
||||
home_dir_buf[entry.name.len + 1 + "package.json".len] = 0;
|
||||
|
||||
var path: [:0]u8 = home_dir_buf[0 .. entry.name.len + 1 + "package.json".len :0];
|
||||
|
||||
folder.accessZ(path, .{
|
||||
.read = true,
|
||||
}) catch continue :loop;
|
||||
|
||||
try examples.append(
|
||||
Example{
|
||||
.name = try filesystem.filename_store.append(@TypeOf(entry.name), entry.name),
|
||||
.version = "",
|
||||
.local = true,
|
||||
.description = "",
|
||||
},
|
||||
);
|
||||
continue :loop;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const examples = try Example.fetchAllLocalAndRemote(ctx, &env_loader, filesystem);
|
||||
Output.printStartEnd(time, std.time.nanoTimestamp());
|
||||
Output.prettyln(" <d>Fetched examples<r>", .{});
|
||||
Output.prettyln("Welcome to Bun! Create a new project by pasting any of the following:\n\n", .{});
|
||||
Output.flush();
|
||||
|
||||
Example.print(examples.items);
|
||||
Example.print(examples.items, null);
|
||||
|
||||
if (env_loader.map.get("HOME")) |homedir| {
|
||||
Output.prettyln(
|
||||
|
||||
@@ -460,6 +460,9 @@ pub const Archive = struct {
|
||||
Status.eof => break :loop,
|
||||
Status.failed, Status.fatal, Status.retry => return error.Fail,
|
||||
else => {
|
||||
// do not use the utf8 name there
|
||||
// it will require us to pull in libiconv
|
||||
// though we should probably validate the utf8 here nonetheless
|
||||
var pathname: [:0]const u8 = std.mem.sliceTo(lib.archive_entry_pathname(entry).?, 0);
|
||||
var tokenizer = std.mem.tokenize(u8, std.mem.span(pathname), std.fs.path.sep_str);
|
||||
comptime var depth_i: usize = 0;
|
||||
@@ -511,6 +514,7 @@ pub const Archive = struct {
|
||||
ctx: ?*Archive.Context,
|
||||
comptime depth_to_skip: usize,
|
||||
comptime close_handles: bool,
|
||||
comptime log: bool,
|
||||
) !u32 {
|
||||
var entry: *lib.archive_entry = undefined;
|
||||
var ext: *lib.archive = undefined;
|
||||
@@ -559,7 +563,9 @@ pub const Archive = struct {
|
||||
const mask = lib.archive_entry_filetype(entry);
|
||||
const size = @intCast(usize, std.math.max(lib.archive_entry_size(entry), 0));
|
||||
if (size > 0) {
|
||||
Output.prettyln(" {s}", .{pathname});
|
||||
if (comptime log) {
|
||||
Output.prettyln(" {s}", .{pathname});
|
||||
}
|
||||
|
||||
const file = dir.createFileZ(pathname, .{ .truncate = true }) catch |err| brk: {
|
||||
switch (err) {
|
||||
|
||||
14
src/main.zig
14
src/main.zig
@@ -37,7 +37,19 @@ pub fn main() anyerror!void {
|
||||
|
||||
Output.Source.set(&output_source);
|
||||
defer Output.flush();
|
||||
try cli.Cli.start(default_allocator, stdout, stderr, MainPanicHandler);
|
||||
cli.Cli.start(default_allocator, stdout, stderr, MainPanicHandler) catch |err| {
|
||||
switch (err) {
|
||||
error.CurrentWorkingDirectoryUnlinked => {
|
||||
Output.prettyError(
|
||||
"\n<r><red>error: <r>The current working directory was deleted, so that command didn't work. Please cd into a different directory and try again.",
|
||||
.{},
|
||||
);
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
|
||||
std.mem.doNotOptimizeAway(JavaScriptVirtualMachine.fetch);
|
||||
std.mem.doNotOptimizeAway(JavaScriptVirtualMachine.init);
|
||||
|
||||
@@ -13,25 +13,20 @@ pub const NPMClient = struct {
|
||||
pnpm,
|
||||
};
|
||||
|
||||
pub fn isYarnBerry(allocator: *std.mem.Allocator, yarn_path: string) bool {
|
||||
// This check adds around 150ms
|
||||
// so...if we do do this, we should do it in a separate thread
|
||||
pub fn isYarnBerry(allocator: *std.mem.Allocator, cwd_dir: string, yarn_path: string) bool {
|
||||
var args = [_]string{ yarn_path, "--version" };
|
||||
var child_process = std.ChildProcess.init(&args, allocator) catch return true;
|
||||
defer child_process.deinit();
|
||||
child_process.cwd_dir = std.fs.cwd();
|
||||
child_process.expand_arg0 = .no_expand;
|
||||
child_process.stdout_behavior = .Pipe;
|
||||
child_process.stderr_behavior = .Pipe;
|
||||
child_process.spawn() catch return true;
|
||||
defer _ = child_process.kill() catch undefined;
|
||||
var term = std.ChildProcess.exec(.{
|
||||
.argv = &args,
|
||||
.allocator = allocator,
|
||||
.cwd = if (cwd_dir.len > 1) std.mem.trimRight(u8, cwd_dir, "/") else cwd_dir,
|
||||
}) catch return true;
|
||||
defer allocator.free(term.stderr);
|
||||
defer allocator.free(term.stdout);
|
||||
|
||||
var path_buf: [512]u8 = undefined;
|
||||
var path_len = child_process.stdout.?.read(&path_buf) catch return true;
|
||||
|
||||
if (path_len == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return path_buf[0] != '1';
|
||||
if (term.stdout.len == 0) return true;
|
||||
return term.stdout[0] != '1';
|
||||
}
|
||||
|
||||
pub fn detect(allocator: *std.mem.Allocator, realpath_buf: *[std.fs.MAX_PATH_BYTES]u8, PATH: string, cwd: string, comptime allow_yarn: bool) !?NPMClient {
|
||||
@@ -40,11 +35,12 @@ pub const NPMClient = struct {
|
||||
// - pnpm if it exists, is the default. its most esoteric, so if you have it installed, you prob want it.
|
||||
// - yarn if it exists and it is yarn 1, its the default (yarn 2 or later is not supported)
|
||||
// - else npm
|
||||
var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
const path: [:0]const u8 = brk: {
|
||||
if (comptime allow_yarn) {
|
||||
break :brk which(
|
||||
const out_path = brk: {
|
||||
var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
const path: [:0]const u8 = if (comptime allow_yarn)
|
||||
which(
|
||||
&path_buf,
|
||||
PATH,
|
||||
cwd,
|
||||
@@ -59,9 +55,9 @@ pub const NPMClient = struct {
|
||||
PATH,
|
||||
cwd,
|
||||
"npm",
|
||||
) orelse "";
|
||||
} else {
|
||||
break :brk which(
|
||||
) orelse ""
|
||||
else
|
||||
which(
|
||||
&path_buf,
|
||||
PATH,
|
||||
cwd,
|
||||
@@ -72,24 +68,22 @@ pub const NPMClient = struct {
|
||||
cwd,
|
||||
"npm",
|
||||
) orelse "";
|
||||
}
|
||||
unreachable;
|
||||
|
||||
var file = std.fs.openFileAbsoluteZ(path, .{ .read = true }) catch return null;
|
||||
defer file.close();
|
||||
break :brk std.os.getFdPath(file.handle, realpath_buf) catch return null;
|
||||
};
|
||||
|
||||
var basename = std.fs.path.basename(path);
|
||||
const basename = std.fs.path.basename(std.mem.span(out_path));
|
||||
if (basename.len == 0) return null;
|
||||
|
||||
if (comptime allow_yarn) {
|
||||
if (std.mem.indexOf(u8, basename, "yarn") != null) {
|
||||
if (isYarnBerry(allocator, path)) {
|
||||
return try detect(allocator, realpath_buf, PATH, cwd, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var file = std.fs.openFileAbsoluteZ(path, .{ .read = true }) catch return null;
|
||||
defer file.close();
|
||||
const out_path = std.os.getFdPath(file.handle, realpath_buf) catch return null;
|
||||
// if (comptime allow_yarn) {
|
||||
// if (std.mem.indexOf(u8, basename, "yarn") != null) {
|
||||
// if (isYarnBerry(allocator, cwd, out_path)) {
|
||||
// return try detect(allocator, realpath_buf, PATH, cwd, false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (strings.contains(basename, "pnpm")) {
|
||||
return NPMClient{ .bin = out_path, .tag = .pnpm };
|
||||
|
||||
Reference in New Issue
Block a user