mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Support passing an absolute path
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# bun create
|
||||
|
||||
`bun create` is a fast way to create a new project from a template. At the time of writing, `bun create react app` runs ~14x faster on my local computer than `yarn create react-app app`. `bun create` currently does no caching (though your npm client does)
|
||||
|
||||
## Usage
|
||||
|
||||
Templates are downloaded from folders inside `examples/` in Bun's GitHub repo. Running `bun create react ./local-path` downloads the `react` folder from `examples/react`.
|
||||
|
||||
Create a new Next.js project:
|
||||
|
||||
```bash
|
||||
bun create next ./app`
|
||||
```
|
||||
|
||||
Create a new React project:
|
||||
|
||||
```bash
|
||||
bun create react ./app
|
||||
```
|
||||
|
||||
To see a list of available templates, run
|
||||
|
||||
```bash
|
||||
bun create
|
||||
```
|
||||
|
||||
### Advanced
|
||||
|
||||
| Flag | Description |
|
||||
| ---------------------- | -------------------------------------- |
|
||||
| --npm | Use `npm` for tasks & install |
|
||||
| --yarn | Use `yarn` for tasks & install |
|
||||
| --pnpm | Use `pnpm` for tasks & install |
|
||||
| --force | Overwrite existing files |
|
||||
| --no-install | Skip installing `node_modules` & tasks |
|
||||
| --no-git | Don't initialize a git repository |
|
||||
| ---------------------- | ----------------------------------- |
|
||||
|
||||
By default, `bun create` will cancel if there are existing files it would overwrite. You can pass `--force` to disable this behavior.
|
||||
|
||||
## Adding a new template
|
||||
|
||||
Clone this repository and a new folder in `examples/` with your new template. The `package.json` must have a `name` that starts with `@bun-examples/`. Do not worry about publishing it, that will happen automaticallly after the PR is merged.
|
||||
|
||||
Make sure to include a `.gitignore` that includes `node_modules` so that `node_modules` aren't checked in to git when people download the template.
|
||||
|
||||
#### Testing your new template
|
||||
|
||||
### Config
|
||||
|
||||
The `bun-create` section of package.json is automatically removed from the `package.json` on disk. This lets you add create-only steps without waiting for an extra package to install.
|
||||
|
||||
There are currently two options:
|
||||
|
||||
- `postinstall`
|
||||
- `preinstall`
|
||||
|
||||
They can be an array of strings or one string. An array of steps will be executed in order.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@bun-examples/next",
|
||||
"version": "0.0.31",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"next": "11.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-is": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^17.0.19",
|
||||
"bun-framework-next": "^0.0.0-21",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"bun-create": {
|
||||
"postinstall": ["bun bun --use next"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By default, all commands run inside the environment exposed by the auto-detected npm client. This incurs a significant performance penalty, something like 150ms spent waiting for the npm client to start on each invocation.
|
||||
|
||||
Any command that starts with `"bun "` will be run without npm, relying on the first `bun` binary in `$PATH`.
|
||||
|
||||
## How `bun create` works
|
||||
|
||||
When you run `bun create ${template} ${destination}`, here's what happens:
|
||||
|
||||
1. GET `registry.npmjs.org/@bun-examples/${template}/latest` and parse it
|
||||
2. GET `registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz`
|
||||
3. Decompress & extract `${template}-${latestVersion}.tgz` into `${destination}`
|
||||
|
||||
- If there are files that would overwrite, warn and exit unless `--force` is passed
|
||||
|
||||
4. Parse the `package.json` (again!), update `name` to be `${basename(destination)}`, remove the `bun-create` section from the `package.json` and save updated `package.json` to disk
|
||||
5. Auto-detect the npm client, preferring `pnpm`, `yarn` (v1), and lastly `npm`
|
||||
6. Run any tasks defined in `"bun-create": { "preinstall" }` with the npm client
|
||||
7. Run `${npmClient} install`
|
||||
8. Run any tasks defined in `"bun-create": { "preinstall" }` with the npm client
|
||||
9. Run `git init; git add -A .; git commit -am "Initial Commit";`.
|
||||
|
||||
- Rename `gitignore` to `.gitignore`. NPM automatically removes `.gitignore` files from appearing in packages.
|
||||
|
||||
10. Done
|
||||
|
||||
`../misctools/publish-examples.js` publishes all examples to npm.
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
# `bun create`
|
||||
|
||||
## Config
|
||||
|
||||
The `bun-create` section of package.json is automatically removed from the final output. This lets you add create-only steps without installing an extra package.
|
||||
|
||||
There are currently two options:
|
||||
|
||||
- `postinstall`
|
||||
- `preinstall`
|
||||
|
||||
They can be an array of strings or one string. An array of strings will be executed one after another.
|
||||
|
||||
Here are examples:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "@bun-examples/next",
|
||||
"version": "0.0.31",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"next": "11.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-is": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^17.0.19",
|
||||
"bun-framework-next": "^0.0.0-21",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"bun-create": {
|
||||
"postinstall": [
|
||||
"bun bun --use next"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By default, all commands run inside the environment exposed by the auto-detected npm client. This incurs a significant performance penalty, something like 150ms wasted on waiting for the npm client to start on each invocation.
|
||||
|
||||
Any command that starts with `"bun "` will be run without npm.
|
||||
|
||||
## How it works
|
||||
|
||||
When you run `bun create ${template} ${destination}`, here's what happens:
|
||||
|
||||
1. GET `registry.npmjs.org/@bun-examples/${template}/latest` and parse it
|
||||
2. GET `registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz`
|
||||
3. Decompress & extract `${template}-${latestVersion}.tgz` into `${destination}`
|
||||
|
||||
- If there are files that would overwrite, warn and exit unless `--force` is passed
|
||||
|
||||
4. Parse the `package.json` (again!), update `name` to be `${basename(destination)}`, remove the `bun-create` section from the `package.json` and save updated `package.json` to disk
|
||||
5. Auto-detect the npm client, preferring `pnpm`, `yarn` (v1), and lastly `npm`
|
||||
6. Run any tasks defined in `"bun-create": { "preinstall" }` with the npm client
|
||||
7. Run `${npmClient} install`
|
||||
8. Run any tasks defined in `"bun-create": { "preinstall" }` with the npm client
|
||||
9. Run `git init; git add -A .; git commit -am "Initial Commit";`.
|
||||
|
||||
- Rename `gitignore` to `.gitignore`. NPM automatically removes `.gitignore` files from appearing in packages.
|
||||
|
||||
10. Done
|
||||
|
||||
`../misctools/publish-examples.js` publishes all examples to npm.
|
||||
@@ -71,5 +71,5 @@ pub fn main() anyerror!void {
|
||||
tarball_buf_list = std.ArrayListUnmanaged(u8){ .capacity = file_buf.len, .items = file_buf };
|
||||
}
|
||||
|
||||
try Archive.extractToDisk(file_buf, folder, 1, false);
|
||||
try Archive.extractToDisk(file_buf, folder, null, 1, false);
|
||||
}
|
||||
|
||||
@@ -95,18 +95,18 @@ const CreateOptions = struct {
|
||||
overwrite: bool = false,
|
||||
skip_git: bool = false,
|
||||
|
||||
pub fn parse(allocator: *std.mem.Allocator) !CreateOptions {
|
||||
const params = comptime [_]clap.Param(clap.Help){
|
||||
clap.parseParam("--help Print this menu") catch unreachable,
|
||||
clap.parseParam("--npm Use npm for tasks & install") catch unreachable,
|
||||
clap.parseParam("--yarn Use yarn for tasks & install") catch unreachable,
|
||||
clap.parseParam("--pnpm Use pnpm for tasks & install") catch unreachable,
|
||||
clap.parseParam("--force Overwrite existing files") catch unreachable,
|
||||
clap.parseParam("--no-install Don't install node_modules") catch unreachable,
|
||||
clap.parseParam("--no-git Don't create a git repository") catch unreachable,
|
||||
clap.parseParam("<POS>... ") catch unreachable,
|
||||
};
|
||||
const params = [_]clap.Param(clap.Help){
|
||||
clap.parseParam("--help Print this menu") catch unreachable,
|
||||
clap.parseParam("--npm Use npm for tasks & install") catch unreachable,
|
||||
clap.parseParam("--yarn Use yarn for tasks & install") catch unreachable,
|
||||
clap.parseParam("--pnpm Use pnpm for tasks & install") catch unreachable,
|
||||
clap.parseParam("--force Overwrite existing files") catch unreachable,
|
||||
clap.parseParam("--no-install Don't install node_modules") catch unreachable,
|
||||
clap.parseParam("--no-git Don't create a git repository") catch unreachable,
|
||||
clap.parseParam("<POS>... ") catch unreachable,
|
||||
};
|
||||
|
||||
pub fn parse(allocator: *std.mem.Allocator, comptime print_flags_only: bool) !CreateOptions {
|
||||
var diag = clap.Diagnostic{};
|
||||
|
||||
var args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag, .allocator = allocator }) catch |err| {
|
||||
@@ -115,8 +115,15 @@ const CreateOptions = struct {
|
||||
return err;
|
||||
};
|
||||
|
||||
if (args.flag("--help")) {
|
||||
clap.help(Output.writer(), ¶ms) catch {};
|
||||
if (args.flag("--help") or comptime print_flags_only) {
|
||||
if (comptime print_flags_only) {
|
||||
clap.help(Output.writer(), params[1..]) catch {};
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Output.prettyln("<r><b>bun create<r> flags:\n", .{});
|
||||
clap.help(Output.writer(), params[1..]) catch {};
|
||||
Output.flush();
|
||||
std.os.exit(0);
|
||||
}
|
||||
|
||||
@@ -153,9 +160,23 @@ 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);
|
||||
var create_options = try CreateOptions.parse(ctx.allocator, false);
|
||||
|
||||
var filesystem = try fs.FileSystem.init1(ctx.allocator, null);
|
||||
var env_loader: DotEnv.Loader = brk: {
|
||||
var map = try ctx.allocator.create(DotEnv.Map);
|
||||
map.* = DotEnv.Map.init(ctx.allocator);
|
||||
|
||||
break :brk DotEnv.Loader.init(map, ctx.allocator);
|
||||
};
|
||||
|
||||
env_loader.loadProcess();
|
||||
|
||||
const template = positionals[0];
|
||||
const dirname = positionals[1];
|
||||
var filename_writer = filesystem.dirname_store;
|
||||
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);
|
||||
@@ -163,7 +184,7 @@ pub const CreateCommand = struct {
|
||||
var node = node_.start("Downloading", 0);
|
||||
|
||||
// alacritty is fast
|
||||
if (std.os.getenvZ("ALACRITTY_LOG") != null) {
|
||||
if (env_loader.map.get("ALACRITTY_LOG") != null) {
|
||||
progress.refresh_rate_ns = std.time.ns_per_ms * 8;
|
||||
}
|
||||
|
||||
@@ -172,115 +193,193 @@ pub const CreateCommand = struct {
|
||||
progress.refresh();
|
||||
}
|
||||
|
||||
var filesystem = try fs.FileSystem.init1(ctx.allocator, null);
|
||||
var package_json_contents: MutableString = undefined;
|
||||
var package_json_file: std.fs.File = undefined;
|
||||
|
||||
var tarball_bytes: MutableString = if (!(strings.eqlComptime(std.fs.path.extension(template), ".tgz") or strings.eqlComptime(std.fs.path.extension(template), ".tar.gz")))
|
||||
try Example.fetch(ctx, template, &progress, &node)
|
||||
else
|
||||
Example.fetchFromDisk(ctx, template, &progress, &node) catch |err| {
|
||||
node.end();
|
||||
progress.refresh();
|
||||
Output.prettyErrorln("Error loading package from disk {s}", .{@errorName(err)});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
if (!std.fs.path.isAbsolute(template)) {
|
||||
var tarball_bytes: MutableString = try Example.fetch(ctx, template, &progress, &node);
|
||||
|
||||
node.end();
|
||||
|
||||
node = progress.root.start(try std.fmt.bufPrint(&extracting_name_buf, "Decompressing {s}", .{template}), 0);
|
||||
node.setCompletedItems(0);
|
||||
node.setEstimatedTotalItems(0);
|
||||
node.activate();
|
||||
progress.refresh();
|
||||
|
||||
var file_buf = try ctx.allocator.alloc(u8, 16384);
|
||||
|
||||
var tarball_buf_list = std.ArrayListUnmanaged(u8){ .capacity = file_buf.len, .items = file_buf };
|
||||
var gunzip = try Zlib.ZlibReaderArrayList.init(tarball_bytes.list.items, &tarball_buf_list, ctx.allocator);
|
||||
try gunzip.readAll();
|
||||
gunzip.deinit();
|
||||
|
||||
node.end();
|
||||
|
||||
node = progress.root.start(try std.fmt.bufPrint(&extracting_name_buf, "Extracting {s}", .{template}), 0);
|
||||
node.setCompletedItems(0);
|
||||
node.setEstimatedTotalItems(0);
|
||||
node.activate();
|
||||
progress.refresh();
|
||||
|
||||
var pluckers = [_]Archive.Plucker{
|
||||
try Archive.Plucker.init("package.json", 2048, ctx.allocator),
|
||||
try Archive.Plucker.init("GETTING_STARTED", 512, ctx.allocator),
|
||||
};
|
||||
|
||||
node.end();
|
||||
var archive_context = Archive.Context{
|
||||
.pluckers = &pluckers,
|
||||
.overwrite_list = std.StringArrayHashMap(void).init(ctx.allocator),
|
||||
};
|
||||
|
||||
node = progress.root.start(try std.fmt.bufPrint(&extracting_name_buf, "Decompressing {s}", .{template}), 0);
|
||||
node.setCompletedItems(0);
|
||||
node.setEstimatedTotalItems(0);
|
||||
node.activate();
|
||||
progress.refresh();
|
||||
if (!create_options.overwrite) {
|
||||
try Archive.getOverwritingFileList(
|
||||
tarball_buf_list.items,
|
||||
destination,
|
||||
&archive_context,
|
||||
@TypeOf(filesystem.dirname_store),
|
||||
filesystem.dirname_store,
|
||||
1,
|
||||
);
|
||||
|
||||
var file_buf = try ctx.allocator.alloc(u8, 16384);
|
||||
if (archive_context.overwrite_list.count() > 0) {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
progress.refresh();
|
||||
|
||||
var tarball_buf_list = std.ArrayListUnmanaged(u8){ .capacity = file_buf.len, .items = file_buf };
|
||||
var gunzip = try Zlib.ZlibReaderArrayList.init(tarball_bytes.list.items, &tarball_buf_list, ctx.allocator);
|
||||
try gunzip.readAll();
|
||||
gunzip.deinit();
|
||||
// Thank you create-react-app for this copy (and idea)
|
||||
Output.prettyErrorln(
|
||||
"<r><red>error<r><d>: <r>The directory <b><green>{s}<r> contains files that could conflict:",
|
||||
.{
|
||||
std.fs.path.basename(destination),
|
||||
},
|
||||
);
|
||||
for (archive_context.overwrite_list.keys()) |path| {
|
||||
if (strings.endsWith(path, std.fs.path.sep_str)) {
|
||||
Output.prettyErrorln("<r> <cyan>{s}<r>", .{path});
|
||||
} else {
|
||||
Output.prettyErrorln("<r> {s}", .{path});
|
||||
}
|
||||
}
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
node.end();
|
||||
|
||||
node = progress.root.start(try std.fmt.bufPrint(&extracting_name_buf, "Extracting {s}", .{template}), 0);
|
||||
node.setCompletedItems(0);
|
||||
node.setEstimatedTotalItems(0);
|
||||
node.activate();
|
||||
progress.refresh();
|
||||
|
||||
var pluckers = [_]Archive.Plucker{
|
||||
try Archive.Plucker.init("package.json", 2048, ctx.allocator),
|
||||
try Archive.Plucker.init("GETTING_STARTED", 512, ctx.allocator),
|
||||
};
|
||||
|
||||
var archive_context = Archive.Context{
|
||||
.pluckers = &pluckers,
|
||||
.overwrite_list = std.StringArrayHashMap(void).init(ctx.allocator),
|
||||
};
|
||||
|
||||
var filename_writer = filesystem.dirname_store;
|
||||
|
||||
const destination = try filesystem.dirname_store.append([]const u8, resolve_path.joinAbs(filesystem.top_level_dir, .auto, dirname));
|
||||
|
||||
if (!create_options.overwrite) {
|
||||
try Archive.getOverwritingFileList(
|
||||
const extracted_file_count = try Archive.extractToDisk(
|
||||
tarball_buf_list.items,
|
||||
destination,
|
||||
&archive_context,
|
||||
@TypeOf(filesystem.dirname_store),
|
||||
filesystem.dirname_store,
|
||||
1,
|
||||
false,
|
||||
);
|
||||
|
||||
if (archive_context.overwrite_list.count() > 0) {
|
||||
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();
|
||||
progress.refresh();
|
||||
|
||||
package_json_contents = plucker.contents;
|
||||
package_json_file = std.fs.File{ .handle = plucker.fd };
|
||||
} else {
|
||||
const template_dir = std.fs.openDirAbsolute(template, .{ .iterate = true }) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
progress.refresh();
|
||||
|
||||
// Thank you create-react-app for this copy (and idea)
|
||||
Output.prettyErrorln(
|
||||
"<r><red>error<r><d>: <r>The directory <b><green>{s}<r> contains files that could conflict:",
|
||||
.{
|
||||
std.fs.path.basename(destination),
|
||||
},
|
||||
);
|
||||
for (archive_context.overwrite_list.keys()) |path| {
|
||||
if (strings.endsWith(path, std.fs.path.sep_str)) {
|
||||
Output.prettyErrorln("<r> <cyan>{s}<r>", .{path});
|
||||
} else {
|
||||
Output.prettyErrorln("<r> {s}", .{path});
|
||||
Output.prettyErrorln("<r><red>{s}<r>: opening dir {s}", .{ @errorName(err), template });
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
};
|
||||
|
||||
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 });
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
};
|
||||
|
||||
var walker = try template_dir.walk(ctx.allocator);
|
||||
defer walker.deinit();
|
||||
while (try walker.next()) |entry| {
|
||||
// TODO: make this not walk these folders entirely
|
||||
// rather than checking each file path.....
|
||||
if (entry.kind != .File or
|
||||
std.mem.indexOf(u8, entry.path, "node_modules") != null or
|
||||
std.mem.indexOf(u8, entry.path, ".git") != null) continue;
|
||||
|
||||
entry.dir.copyFile(entry.basename, destination_dir, entry.path, .{}) catch {
|
||||
if (std.fs.path.dirname(entry.path)) |entry_dirname| {
|
||||
destination_dir.makePath(entry_dirname) 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 });
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
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)});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
};
|
||||
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)});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
};
|
||||
|
||||
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", .{});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
}
|
||||
package_json_contents = try MutableString.init(ctx.allocator, stat.size);
|
||||
package_json_contents.inflate(package_json_file.readAll(package_json_contents.list.items) catch |err| {
|
||||
node.end();
|
||||
progress.root.end();
|
||||
progress.refresh();
|
||||
|
||||
Output.prettyErrorln("Error reading package.json: <r><red>{s}", .{@errorName(err)});
|
||||
Output.flush();
|
||||
std.os.exit(1);
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
const extracted_file_count = try Archive.extractToDisk(
|
||||
tarball_buf_list.items,
|
||||
destination,
|
||||
&archive_context,
|
||||
1,
|
||||
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();
|
||||
progress.refresh();
|
||||
|
||||
var source = logger.Source.initPathString("package.json", plucker.contents.toOwnedSliceLeaky());
|
||||
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();
|
||||
@@ -399,7 +498,6 @@ pub const CreateCommand = struct {
|
||||
node.name = "Saving package.json";
|
||||
progress.maybeRefresh();
|
||||
|
||||
const package_json_file = std.fs.File{ .handle = plucker.fd };
|
||||
var package_json_writer = JSPrinter.NewFileWriter(package_json_file);
|
||||
|
||||
_ = JSPrinter.printJSON(@TypeOf(package_json_writer), package_json_writer, package_json_expr, &source) catch |err| {
|
||||
@@ -408,15 +506,6 @@ pub const CreateCommand = struct {
|
||||
std.os.exit(1);
|
||||
};
|
||||
|
||||
var env_loader: DotEnv.Loader = brk: {
|
||||
var map = try ctx.allocator.create(DotEnv.Map);
|
||||
map.* = DotEnv.Map.init(ctx.allocator);
|
||||
|
||||
break :brk DotEnv.Loader.init(map, ctx.allocator);
|
||||
};
|
||||
|
||||
env_loader.loadProcess();
|
||||
|
||||
const PATH = env_loader.map.get("PATH") orelse "";
|
||||
|
||||
var npm_client_: ?NPMClient = null;
|
||||
@@ -457,6 +546,7 @@ pub const CreateCommand = struct {
|
||||
node.end();
|
||||
|
||||
if (npm_client_) |npm_client| {
|
||||
const start_time = std.time.nanoTimestamp();
|
||||
var install_args = [_]string{ npm_client.bin, "install" };
|
||||
Output.printError("\n", .{});
|
||||
Output.flush();
|
||||
@@ -468,12 +558,18 @@ pub const CreateCommand = struct {
|
||||
process.cwd = destination;
|
||||
|
||||
defer {
|
||||
Output.printErrorln("\n", .{});
|
||||
Output.printStartEnd(start_time, std.time.nanoTimestamp());
|
||||
Output.prettyError(" <r><d>{s} install<r>\n", .{@tagName(npm_client.tag)});
|
||||
Output.flush();
|
||||
|
||||
Output.print("\n", .{});
|
||||
Output.flush();
|
||||
}
|
||||
defer process.deinit();
|
||||
|
||||
var term = try process.spawnAndWait();
|
||||
|
||||
_ = process.kill() catch undefined;
|
||||
} else if (!create_options.skip_install) {
|
||||
progress.log("Failed to detect npm client. Tried pnpm, yarn, and npm.\n", .{});
|
||||
@@ -530,7 +626,7 @@ pub const CreateCommand = struct {
|
||||
|
||||
Output.printError("\n", .{});
|
||||
Output.printStartEnd(ctx.start_time, std.time.nanoTimestamp());
|
||||
Output.prettyErrorln(" <r><d>bun create {s} <r><d><b>({d} files)<r>", .{ template, extracted_file_count });
|
||||
Output.prettyErrorln(" <r><d>bun create {s}<r>", .{template});
|
||||
Output.flush();
|
||||
}
|
||||
};
|
||||
@@ -848,6 +944,8 @@ pub const CreateListExamplesCommand = struct {
|
||||
|
||||
Example.print(examples);
|
||||
|
||||
_ = try CreateOptions.parse(ctx.allocator, true);
|
||||
|
||||
Output.pretty("<d>To add a new template, git clone https://github.com/jarred-sumner/bun, add a new folder to the \"examples\" folder, and submit a PR.<r>", .{});
|
||||
Output.flush();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user