From 18b521d9b875f4514e413dbe9b614309fd1618aa Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Tue, 12 Sep 2023 21:51:49 -0700 Subject: Various docs (#5201) * Updates * Improve jest guide * Improve --- docs/guides/install/trusted.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/guides/install/trusted.md (limited to 'docs/guides/install') diff --git a/docs/guides/install/trusted.md b/docs/guides/install/trusted.md new file mode 100644 index 000000000..3dc14aa94 --- /dev/null +++ b/docs/guides/install/trusted.md @@ -0,0 +1,29 @@ +--- +name: Add a trusted dependency +--- + +Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts for installed dependencies, such as `postinstall` and `node-gyp` builds. These scripts represent a potential security risk, as they can execute arbitrary code on your machine. + +--- + +To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json. + +```json-diff + { + "name": "my-app", + "version": "1.0.0", ++ "trustedDependencies": ["my-trusted-package"] + } +``` + + + +--- + +Note that this only allows lifecycle scripts for the specific package listed in `trustedDependencies`, _not_ the dependencies of that dependency! + +Soon, Bun will include a built-in allow-list that automatically allows lifecycle scripts to be run by popular packages that are known to be safe. This is still under development. + +--- + +See [Docs > Package manager > Trusted dependencies](/docs/cli/install#trusted-dependencies) for complete documentation of trusted dependencies. -- cgit v1.2.3 From c99caccdb2e1bb961b13766ec7ebb9907763e364 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Wed, 13 Sep 2023 20:43:39 -0700 Subject: More docs & helptext cleanup (#5229) * wip * Flesh out resolution docs * Polish * More * WIP * WIP * WIP * Document --watch --- docs/api/file-io.md | 2 +- docs/bundler/index.md | 8 ++++ docs/bundler/vs-esbuild.md | 4 +- docs/guides/ecosystem/nextjs.md | 13 ++++++- docs/guides/install/registry-scope.md | 6 ++- docs/guides/install/workspaces.md | 4 +- docs/guides/runtime/typescript.md | 69 +++++++++++++++++++++++++++++++++++ docs/installation.md | 2 + docs/runtime/modules.md | 26 +++++++++++-- src/cli.zig | 17 +++++---- 10 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 docs/guides/runtime/typescript.md (limited to 'docs/guides/install') diff --git a/docs/api/file-io.md b/docs/api/file-io.md index e6902cc6a..f37473c33 100644 --- a/docs/api/file-io.md +++ b/docs/api/file-io.md @@ -2,7 +2,7 @@ -**Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir`, you can use Bun's [nearly complete](/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module. +**Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir` or `readdir`, you can use Bun's [nearly complete](/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module. {% /callout %} diff --git a/docs/bundler/index.md b/docs/bundler/index.md index 585a6ac52..8db531ce5 100644 --- a/docs/bundler/index.md +++ b/docs/bundler/index.md @@ -132,6 +132,14 @@ Visit `http://localhost:5000` to see your bundled app in action. {% /details %} +## Watch mode + +Like the runtime and test runner, the bundler supports watch mode natively. + +```sh +$ bun build ./index.tsx --outdir ./out --watch +``` + ## Content types Like the Bun runtime, the bundler supports an array of file types out of the box. The following table breaks down the bundler's set of standard "loaders". Refer to [Bundler > File types](/docs/runtime/loaders) for full documentation. diff --git a/docs/bundler/vs-esbuild.md b/docs/bundler/vs-esbuild.md index 44ab835a3..67bed3dc3 100644 --- a/docs/bundler/vs-esbuild.md +++ b/docs/bundler/vs-esbuild.md @@ -130,8 +130,8 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot --- - `--watch` -- n/a -- Not applicable +- `--watch` +- No differences --- diff --git a/docs/guides/ecosystem/nextjs.md b/docs/guides/ecosystem/nextjs.md index a455eb23e..4d3be8cfc 100644 --- a/docs/guides/ecosystem/nextjs.md +++ b/docs/guides/ecosystem/nextjs.md @@ -3,7 +3,7 @@ name: Build an app with Next.js and Bun --- {% callout %} -Next.js currently relies on Node.js APIs that Bun does not yet implement. The guide below uses Bun to initialize a project and install dependencies, but it uses Node.js to run the dev server. +The Next.js [App Router](https://nextjs.org/docs/app) currently relies on Node.js APIs that Bun does not yet implement. The guide below uses Bun to initialize a project and install dependencies, but it uses Node.js to run the dev server. {% /callout %} --- @@ -23,7 +23,16 @@ Creating a new Next.js app in /path/to/my-app. --- -To start the dev server, run `bun run dev` from the project root. +To start the dev server with Bun, run `bun --bun run dev` from the project root. + +```sh +$ cd my-app +$ bun --bun run dev +``` + +--- + +To run the dev server with Node.js instead, omit `--bun`. ```sh $ cd my-app diff --git a/docs/guides/install/registry-scope.md b/docs/guides/install/registry-scope.md index 48f7dee79..68d8cf942 100644 --- a/docs/guides/install/registry-scope.md +++ b/docs/guides/install/registry-scope.md @@ -11,7 +11,11 @@ Bun does not read `.npmrc` files; instead private registries are configured via # as an object with username/password # you can reference environment variables -"@myorg2" = { username = "myusername", password = "$npm_pass", url = "https://registry.myorg.com/" } +"@myorg2" = { + username = "myusername", + password = "$npm_pass", + url = "https://registry.myorg.com/" +} # as an object with token "@myorg3" = { token = "$npm_token", url = "https://registry.myorg.com/" } diff --git a/docs/guides/install/workspaces.md b/docs/guides/install/workspaces.md index f87c1e337..4628d6aaa 100644 --- a/docs/guides/install/workspaces.md +++ b/docs/guides/install/workspaces.md @@ -4,6 +4,8 @@ name: Configuring a monorepo using workspaces Bun's package manager supports npm `"workspaces"`. This allows you to split a codebase into multiple distinct "packages" that live in the same repository, can depend on each other, and (when possible) share a `node_modules` directory. +Clone [this sample project](https://github.com/colinhacks/bun-workspaces) to experiment with workspaces. + --- The root `package.json` should not contain any `"dependencies"`, `"devDependencies"`, etc. Each individual package should be self-contained and declare its own dependencies. Similarly, it's conventional to declare `"private": true` to avoid accidentally publishing the root package to `npm`. @@ -37,7 +39,7 @@ It's common to place all packages in a `packages` directory. The `"workspaces"` To add one workspace as a dependency of another, modify its `package.json`. Here we're adding `stuff-a` as a dependency of `stuff-b`. -```json#packages/stuff-b/package.json +```json-diff#packages/stuff-b/package.json { "name": "stuff-b", "dependencies": { diff --git a/docs/guides/runtime/typescript.md b/docs/guides/runtime/typescript.md new file mode 100644 index 000000000..f6afe02c1 --- /dev/null +++ b/docs/guides/runtime/typescript.md @@ -0,0 +1,69 @@ +--- +name: Install TypeScript declarations for Bun +--- + +To install TypeScript definitions for Bun's built-in APIs in your project, install `bun-types`. + +```sh +$ bun add -d bun-types # dev dependency +``` + +--- + +Then include `"bun-types"` in the `compilerOptions.types` in your `tsconfig.json`: + +```json-diff + { + "compilerOptions": { ++ "types": ["bun-types"] + } + } +``` + +--- + +Unfortunately, setting a value for `"types"` means that TypeScript will ignore other global type definitions, including `lib: ["dom"]`. If you need to add DOM types into your project, add the following [triple-slash directives](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html) at the top of any TypeScript file in your project. + +```ts +/// +/// +``` + +--- + +Below is the full set of recommended `compilerOptions` for a Bun project. With this `tsconfig.json`, you can use top-level await, extensioned or extensionless imports, and JSX. + +```jsonc +{ + "compilerOptions": { + // add Bun type definitions + "types": ["bun-types"], + + // enable latest features + "lib": ["esnext"], + "module": "esnext", + "target": "esnext", + + // if TS 5.x+ + "moduleResolution": "bundler", + "noEmit": true, + "allowImportingTsExtensions": true, + "moduleDetection": "force", + // if TS 4.x or earlier + // "moduleResolution": "nodenext", + + "jsx": "react-jsx", // support JSX + "allowJs": true, // allow importing `.js` from `.ts` + "esModuleInterop": true, // allow default imports for CommonJS modules + + // best practices + "strict": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + } +} +``` + +--- + +Refer to [Ecosystem > TypeScript](/docs/runtime/typescript) for a complete guide to TypeScript support in Bun. diff --git a/docs/installation.md b/docs/installation.md index df4d12cea..c0b823623 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -10,6 +10,8 @@ Bun ships as a single executable that can be installed a few different ways. ```bash#macOS/Linux_(curl) $ curl -fsSL https://bun.sh/install | bash # for macOS, Linux, and WSL +# to install a specific version +$ curl -fsSL https://bun.sh/install | bash -s "bun-v1.0.0" ``` ```bash#NPM diff --git a/docs/runtime/modules.md b/docs/runtime/modules.md index a876a5a2b..fe8bb8c64 100644 --- a/docs/runtime/modules.md +++ b/docs/runtime/modules.md @@ -184,18 +184,38 @@ Once it finds the `foo` package, Bun reads the `package.json` to determine how t Whichever one of these conditions occurs _first_ in the `package.json` is used to determine the package's entrypoint. -Bun respects subpath [`"exports"`](https://nodejs.org/api/packages.html#subpath-exports) and [`"imports"`](https://nodejs.org/api/packages.html#imports). Specifying any subpath in the `"exports"` map will prevent other subpaths from being importable. +Bun respects subpath [`"exports"`](https://nodejs.org/api/packages.html#subpath-exports) and [`"imports"`](https://nodejs.org/api/packages.html#imports). ```jsonc#package.json { "name": "foo", "exports": { - ".": "./index.js", - "./package.json": "./package.json" // subpath + ".": "./index.js" } } ``` +Subpath imports and conditional imports work in conjunction with each other. + +``` +{ + "name": "foo", + "exports": { + ".": { + "import": "./index.mjs", + "require": "./index.js" + } + } +} +``` + +As in Node.js, Specifying any subpath in the `"exports"` map will prevent other subpaths from being importable; you can only import files that are explicitly exported. Given the `package.json` above: + +```ts +import stuff from "foo"; // this works +import stuff from "foo/index.mjs"; // this doesn't +``` + {% callout %} **Shipping TypeScript** — Note that Bun supports the special `"bun"` export condition. If your library is written in TypeScript, you can publish your (un-transpiled!) TypeScript files to `npm` directly. If you specify your package's `*.ts` entrypoint in the `"bun"` condition, Bun will directly import and execute your TypeScript source files. {% /callout %} diff --git a/src/cli.zig b/src/cli.zig index 889da9320..6f05bd8cb 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -75,6 +75,7 @@ pub const Cli = struct { \\ --target The intended execution environment for the bundle. \\ ("browser", "bun" or "node") \\ --splitting Enable code splitting (requires --outdir) + \\ --watch Run bundler in watch mode \\ \\Examples: \\ Frontend web apps: @@ -168,7 +169,7 @@ pub const Arguments = struct { clap.parseParam("-h, --help Display this help and exit.") catch unreachable, clap.parseParam("-b, --bun Force a script or package to use Bun's runtime instead of Node.js (via symlinking node)") catch unreachable, clap.parseParam("--cwd Absolute path to resolve files & entry points from. This just changes the process' cwd.") catch unreachable, - clap.parseParam("-c, --config ? Config file to load bun from (e.g. -c bunfig.toml") catch unreachable, + clap.parseParam("-c, --config ? Config file to load Bun from (e.g. -c bunfig.toml") catch unreachable, clap.parseParam("--extension-order ... Defaults to: .tsx,.ts,.jsx,.js,.json ") catch unreachable, clap.parseParam("--jsx-factory Changes the function called when compiling JSX elements using the classic JSX runtime") catch unreachable, clap.parseParam("--jsx-fragment Changes the function called when compiling JSX fragments") catch unreachable, @@ -184,7 +185,7 @@ pub const Arguments = struct { clap.parseParam("-e, --external ... Exclude module from transpilation (can use * wildcards). ex: -e react") catch unreachable, clap.parseParam("-l, --loader ... Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: js, jsx, ts, tsx, json, toml, text, file, wasm, napi") catch unreachable, clap.parseParam("-u, --origin Rewrite import URLs to start with --origin. Default: \"\"") catch unreachable, - clap.parseParam("-p, --port Port to serve bun's dev server on. Default: \"3000\"") catch unreachable, + clap.parseParam("-p, --port Port to serve Bun's dev server on. Default: \"3000\"") catch unreachable, clap.parseParam("--smol Use less memory, but run garbage collection more often") catch unreachable, clap.parseParam("--minify Minify (experimental)") catch unreachable, clap.parseParam("--minify-syntax Minify syntax and inline data (experimental)") catch unreachable, @@ -201,13 +202,13 @@ pub const Arguments = struct { // note: we are keeping --port and --origin as it can be reused for bun // build and elsewhere pub const not_bun_dev_flags = [_]ParamType{ - clap.parseParam("--hot Enable auto reload in bun's JavaScript runtime") catch unreachable, - clap.parseParam("--watch Automatically restart bun's JavaScript runtime on file change") catch unreachable, - clap.parseParam("--no-install Disable auto install in bun's JavaScript runtime") catch unreachable, - clap.parseParam("-i Automatically install dependencies and use global cache in bun's runtime, equivalent to --install=fallback") catch unreachable, + clap.parseParam("--hot Enable auto reload in the Bun runtime, test runner, or bundler") catch unreachable, + clap.parseParam("--watch Automatically restart the process on file change") catch unreachable, + clap.parseParam("--no-install Disable auto install in the Bun runtime") catch unreachable, + clap.parseParam("-i Automatically install dependencies and use global cache in Bun's runtime, equivalent to --install=fallback") catch unreachable, clap.parseParam("--install Install dependencies automatically when no node_modules are present, default: \"auto\". \"force\" to ignore node_modules, fallback to install any missing") catch unreachable, - clap.parseParam("--prefer-offline Skip staleness checks for packages in bun's JavaScript runtime and resolve from disk") catch unreachable, - clap.parseParam("--prefer-latest Use the latest matching versions of packages in bun's JavaScript runtime, always checking npm") catch unreachable, + clap.parseParam("--prefer-offline Skip staleness checks for packages in the Bun runtime and resolve from disk") catch unreachable, + clap.parseParam("--prefer-latest Use the latest matching versions of packages in the Bun runtime, always checking npm") catch unreachable, clap.parseParam("--silent Don't repeat the command for bun run") catch unreachable, }; -- cgit v1.2.3 From 07b10bbc16ab16ba73f3990f3b888e98661aabea Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Thu, 14 Sep 2023 17:28:03 -0700 Subject: Clean up trustedDependencies guide --- docs/guides/install/trusted.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/trusted.md b/docs/guides/install/trusted.md index 3dc14aa94..d0d841eea 100644 --- a/docs/guides/install/trusted.md +++ b/docs/guides/install/trusted.md @@ -4,10 +4,25 @@ name: Add a trusted dependency Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts for installed dependencies, such as `postinstall` and `node-gyp` builds. These scripts represent a potential security risk, as they can execute arbitrary code on your machine. +{% callout %} +Soon, Bun will include a built-in allow-list that automatically allows lifecycle scripts to be run by popular packages that are known to be safe. This is still under development. +{% /callout %} + +--- + +If you are seeing one of the following errors, you are probably trying to use a package that uses `postinstall` to work properly: + +- `error: could not determine executable to run for package` +- `InvalidExe` + --- To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json. +Note that this only allows lifecycle scripts for the specific package listed in `trustedDependencies`, _not_ the dependencies of that dependency! + + + ```json-diff { "name": "my-app", @@ -16,14 +31,20 @@ To tell Bun to allow lifecycle scripts for a particular package, add the package } ``` - +--- + +Once this is added, run a fresh install. Bun will re-install your dependencies and properly install + +```sh +$ rm -rf node_modules +$ rm bun.lockb +$ bun install +``` --- Note that this only allows lifecycle scripts for the specific package listed in `trustedDependencies`, _not_ the dependencies of that dependency! -Soon, Bun will include a built-in allow-list that automatically allows lifecycle scripts to be run by popular packages that are known to be safe. This is still under development. - --- See [Docs > Package manager > Trusted dependencies](/docs/cli/install#trusted-dependencies) for complete documentation of trusted dependencies. -- cgit v1.2.3 From 5e6490d905805f244746d21b90e62b0422ffbc68 Mon Sep 17 00:00:00 2001 From: Bruce Denham Date: Sat, 16 Sep 2023 12:21:42 -0500 Subject: Update workspaces.md to reflect "workspace:*" syntax (#5555) --- docs/guides/install/workspaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/workspaces.md b/docs/guides/install/workspaces.md index 4628d6aaa..b271a33b0 100644 --- a/docs/guides/install/workspaces.md +++ b/docs/guides/install/workspaces.md @@ -37,13 +37,13 @@ It's common to place all packages in a `packages` directory. The `"workspaces"` --- -To add one workspace as a dependency of another, modify its `package.json`. Here we're adding `stuff-a` as a dependency of `stuff-b`. +To add dependencies between workspaces, use the `"workspace:*"` syntax. Here we're adding `stuff-a` as a dependency of `stuff-b`. ```json-diff#packages/stuff-b/package.json { "name": "stuff-b", "dependencies": { -+ "stuff-a": "*" ++ "stuff-a": "workspace:*" } } ``` -- cgit v1.2.3 From cc54b62fac41c0977c7dfc4c6ba550a6408fa15f Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Mon, 18 Sep 2023 23:27:02 -0700 Subject: Encode slashes in package names in the registry manifest request (#5716) * Encode slashes in package names in the registry manifest request Co-Authored-By: Max Brosnahan <1177034+gingermusketeer@users.noreply.github.com> * Update dummy.registry.ts * Fix tests * Add guide for Azure Artifacts * Update azure-artifacts.md * Update azure-artifacts.md * Typo --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: Max Brosnahan <1177034+gingermusketeer@users.noreply.github.com> --- docs/guides/install/azure-artifacts.md | 53 ++++++++++++++++++++++++++++++++++ src/env_loader.zig | 7 ++++- src/install/install.zig | 15 +++++++++- src/install/npm.zig | 11 +++++++ test/cli/install/bun-add.test.ts | 4 +-- test/cli/install/bun-install.test.ts | 10 +++---- test/cli/install/bun-update.test.ts | 4 +-- test/cli/install/dummy.registry.ts | 11 ++++--- 8 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 docs/guides/install/azure-artifacts.md (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md new file mode 100644 index 000000000..5205e4e13 --- /dev/null +++ b/docs/guides/install/azure-artifacts.md @@ -0,0 +1,53 @@ +--- +name: Using bun install with an Azure Artifacts npm registry +--- + +[Azure Artifacts](https://azure.microsoft.com/en-us/products/devops/artifacts) is a package management system for Azure DevOps. It allows you to host your own private npm registry, npm packages, and other types of packages as well. + +To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents: + +### Configure with bunfig.toml + +```toml#bunfig.toml +[install.registry] +url = "https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry" +username = "my-azure-artifacts-user" +password = "$NPM_PASSWORD" +``` + +Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. + +Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. + +Note: **password must not be base64 encoded**. In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. + +To un-base64 encode a password, you can open your browser console and run: + +```js +atob("base64-encoded-password"); +``` + +If it ends with `==`, it probably is base64 encoded. + +### Configure with environment variables + +You can also use an environment variable to configure Azure Artifacts with bun install. + +Like with the `npm` CLI, the environment variable to use is `NPM_CONFIG_REGISTRY`. + +The URL should include `:username` and `:_password` as query parameters. For example: + +```bash +NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=my-azure-artifacts-user:_password=my-azure-artifacts-password +``` + +Make sure to: + +- Replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234` +- Replace `my-azure-artifacts-password` with the non-base64 encoded password for your Azure Artifacts npm registry. If it ends with `==`, it probably is base64 encoded. + +To un-base64 encode a password, you can open your browser console and run: + +```js +atob("base64-encoded-password"); +``` diff --git a/src/env_loader.zig b/src/env_loader.zig index 6957a1a26..213d14ab8 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -172,7 +172,12 @@ pub const Loader = struct { } pub fn getAuto(this: *const Loader, key: string) string { - return this.get(key) orelse key; + // If it's "" or "$", it's not a variable + if (key.len < 2 or key[0] != '$') { + return key; + } + + return this.get(key[1..]) orelse key; } /// Load values from the environment into Define. diff --git a/src/install/install.zig b/src/install/install.zig index 722d46be1..158130b25 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -252,9 +252,22 @@ const NetworkTask = struct { warn_on_error: bool, ) !void { this.url_buf = blk: { + + // Not all registries support scoped package names when fetching the manifest. + // registry.npmjs.org supports both "@storybook%2Faddons" and "@storybook/addons" + // Other registries like AWS codeartifact only support the former. + // "npm" CLI requests the manifest with the encoded name. + var arena = std.heap.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + var stack_fallback_allocator = std.heap.stackFallback(512, arena.allocator()); + var encoded_name = name; + if (strings.containsChar(name, '/')) { + encoded_name = try std.mem.replaceOwned(u8, stack_fallback_allocator.get(), name, "/", "%2f"); + } + const tmp = bun.JSC.URL.join( bun.String.fromUTF8(scope.url.href), - bun.String.fromUTF8(name), + bun.String.fromUTF8(encoded_name), ); defer tmp.deref(); diff --git a/src/install/npm.zig b/src/install/npm.zig index 9f3f2952c..fec545b0c 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -68,6 +68,17 @@ pub const Registry = struct { pub fn fromAPI(name: string, registry_: Api.NpmRegistry, allocator: std.mem.Allocator, env: *DotEnv.Loader) !Scope { var registry = registry_; + + // Support $ENV_VAR for registry URLs + if (strings.startsWithChar(registry_.url, '$')) { + // If it became "$ENV_VAR/", then we need to remove the trailing slash + if (env.get(strings.trim(registry_.url[1..], "/"))) |replaced_url| { + if (replaced_url.len > 1) { + registry.url = replaced_url; + } + } + } + var url = URL.parse(registry.url); var auth: string = ""; diff --git a/test/cli/install/bun-add.test.ts b/test/cli/install/bun-add.test.ts index 4461584e4..c00c0ba7b 100644 --- a/test/cli/install/bun-add.test.ts +++ b/test/cli/install/bun-add.test.ts @@ -232,11 +232,11 @@ it("should handle @scoped names", async () => { }); expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.split(/\r?\n/)).toContain('error: package "@bar/baz" not found localhost/@bar/baz 404'); + expect(err.split(/\r?\n/)).toContain('error: package "@bar/baz" not found localhost/@bar%2fbaz 404'); expect(stdout).toBeDefined(); expect(await new Response(stdout).text()).toBe(""); expect(await exited).toBe(1); - expect(urls.sort()).toEqual([`${root_url}/@bar/baz`]); + expect(urls.sort()).toEqual([`${root_url}/@bar%2fbaz`]); expect(requested).toBe(1); try { await access(join(package_dir, "bun.lockb")); diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index c4accb1b4..25cd0aca7 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -39,7 +39,7 @@ it("should report connection errors", async () => { ` [install] cache = false -registry = "http://localhost:${server.port}/" +registry = "http://${server.hostname}:${server.port}/" `, ); await writeFile( @@ -62,7 +62,7 @@ registry = "http://localhost:${server.port}/" }); expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.split(/\r?\n/)).toContain("error: ConnectionClosed downloading package manifest bar"); + expect(err.split(/\r?\n/)).toContain("error: ConnectionRefused downloading package manifest bar"); expect(stdout).toBeDefined(); expect(await new Response(stdout).text()).toBeEmpty(); expect(await exited).toBe(1); @@ -112,7 +112,7 @@ it("should handle missing package", async () => { it("should handle @scoped authentication", async () => { let seen_token = false; - const url = `${root_url}/@foo/bar`; + const url = `${root_url}/@foo%2fbar`; const urls: string[] = []; setHandler(async request => { expect(request.method).toBe("GET"); @@ -2197,7 +2197,7 @@ it("should handle unscoped alias on scoped dependency", async () => { " 1 packages installed", ]); expect(await exited).toBe(0); - expect(urls.sort()).toEqual([`${root_url}/@barn/moo`, `${root_url}/@barn/moo-0.1.0.tgz`]); + expect(urls.sort()).toEqual([`${root_url}/@barn%2fmoo`, `${root_url}/@barn/moo-0.1.0.tgz`]); expect(requested).toBe(2); expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "@barn", "moo"]); expect(await readdirSorted(join(package_dir, "node_modules", "@barn"))).toEqual(["moo"]); @@ -2327,7 +2327,7 @@ it("should handle aliased dependency with existing lockfile", async () => { ]); expect(await exited1).toBe(0); expect(urls.sort()).toEqual([ - `${root_url}/@barn/moo`, + `${root_url}/@barn%2fmoo`, `${root_url}/@barn/moo-0.1.0.tgz`, `${root_url}/bar`, `${root_url}/bar-0.0.2.tgz`, diff --git a/test/cli/install/bun-update.test.ts b/test/cli/install/bun-update.test.ts index 2a6ee4eaf..ff8e22c37 100644 --- a/test/cli/install/bun-update.test.ts +++ b/test/cli/install/bun-update.test.ts @@ -185,7 +185,7 @@ it("should update to latest versions of dependencies", async () => { ]); expect(await exited1).toBe(0); expect(urls.sort()).toEqual([ - `${root_url}/@barn/moo`, + `${root_url}/@barn%2fmoo`, `${root_url}/@barn/moo-0.1.0.tgz`, `${root_url}/baz`, `${root_url}/baz-0.0.3.tgz`, @@ -236,7 +236,7 @@ it("should update to latest versions of dependencies", async () => { ]); expect(await exited2).toBe(0); expect(urls.sort()).toEqual([ - `${root_url}/@barn/moo`, + `${root_url}/@barn%2fmoo`, `${root_url}/@barn/moo-0.1.0.tgz`, `${root_url}/baz`, `${root_url}/baz-0.0.5.tgz`, diff --git a/test/cli/install/dummy.registry.ts b/test/cli/install/dummy.registry.ts index 69af0c381..5d0982f57 100644 --- a/test/cli/install/dummy.registry.ts +++ b/test/cli/install/dummy.registry.ts @@ -34,16 +34,19 @@ export let root_url: string; export function dummyRegistry(urls: string[], info: any = { "0.0.2": {} }) { const _handler: Handler = async request => { urls.push(request.url); + const url = request.url.replaceAll("%2f", "/"); + expect(request.method).toBe("GET"); - if (request.url.endsWith(".tgz")) { - return new Response(file(join(import.meta.dir, basename(request.url).toLowerCase()))); + if (url.endsWith(".tgz")) { + return new Response(file(join(import.meta.dir, basename(url).toLowerCase()))); } expect(request.headers.get("accept")).toBe( "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*", ); expect(request.headers.get("npm-auth-type")).toBe(null); expect(await request.text()).toBe(""); - const name = request.url.slice(request.url.indexOf("/", root_url.length) + 1); + + const name = url.slice(url.indexOf("/", root_url.length) + 1); const versions: Record = {}; let version; for (version in info) { @@ -52,7 +55,7 @@ export function dummyRegistry(urls: string[], info: any = { "0.0.2": {} }) { name, version, dist: { - tarball: `${request.url}-${info[version].as ?? version}.tgz`, + tarball: `${url}-${info[version].as ?? version}.tgz`, }, ...info[version], }; -- cgit v1.2.3 From 9b7fb8b0f3edbd1fb049a78f24063a1e8389086a Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:42:34 -0700 Subject: Update azure-artifacts.md --- docs/guides/install/azure-artifacts.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index 5205e4e13..0deb477c4 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -19,15 +19,6 @@ Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts usernam Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. -Note: **password must not be base64 encoded**. In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. - -To un-base64 encode a password, you can open your browser console and run: - -```js -atob("base64-encoded-password"); -``` - -If it ends with `==`, it probably is base64 encoded. ### Configure with environment variables @@ -46,8 +37,20 @@ Make sure to: - Replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234` - Replace `my-azure-artifacts-password` with the non-base64 encoded password for your Azure Artifacts npm registry. If it ends with `==`, it probably is base64 encoded. +### Don't base64 encode the password + +In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. + To un-base64 encode a password, you can open your browser console and run: ```js atob("base64-encoded-password"); ``` + +You can also use the `base64` command line tool, but doing so means it may be saved in your terminal history which is not recommended: + +```bash +echo "base64-encoded-password" | base64 --decode +``` + +If it ends with `==`, it probably is base64 encoded. \ No newline at end of file -- cgit v1.2.3 From 8b690aaf0d0ae406883e1db82ea882b1fb238285 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:43:06 -0700 Subject: Update azure-artifacts.md --- docs/guides/install/azure-artifacts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index 0deb477c4..1e12b9486 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -35,7 +35,7 @@ NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packagin Make sure to: - Replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234` -- Replace `my-azure-artifacts-password` with the non-base64 encoded password for your Azure Artifacts npm registry. If it ends with `==`, it probably is base64 encoded. +- Replace `my-azure-artifacts-password` with the non-base64 encoded password for your Azure Artifacts npm registry ### Don't base64 encode the password -- cgit v1.2.3 From 63afadcb91d9c8b623da2761f7c7628634baab22 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:46:42 -0700 Subject: Update azure-artifacts.md --- docs/guides/install/azure-artifacts.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index 1e12b9486..716d8dd3a 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -12,6 +12,7 @@ To use it with `bun install`, add a `bunfig.toml` file to your project with the [install.registry] url = "https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry" username = "my-azure-artifacts-user" +# Bun v1.0.3+ supports using an environment variable here password = "$NPM_PASSWORD" ``` -- cgit v1.2.3 From db1263662c6bb4d39a1ebb2e623ccff03cc3bda9 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:49:42 -0700 Subject: Update azure-artifacts.md --- docs/guides/install/azure-artifacts.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index 716d8dd3a..bc2e777f6 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -8,6 +8,8 @@ To use it with `bun install`, add a `bunfig.toml` file to your project with the ### Configure with bunfig.toml +Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. + ```toml#bunfig.toml [install.registry] url = "https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry" @@ -16,8 +18,6 @@ username = "my-azure-artifacts-user" password = "$NPM_PASSWORD" ``` -Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. - Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. @@ -29,7 +29,7 @@ Like with the `npm` CLI, the environment variable to use is `NPM_CONFIG_REGISTRY The URL should include `:username` and `:_password` as query parameters. For example: -```bash +```bash#shell NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=my-azure-artifacts-user:_password=my-azure-artifacts-password ``` -- cgit v1.2.3 From b34da183bdb5b1bfbd9bc68bfeebc7aa9d2f9a8e Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:50:37 -0700 Subject: Update azure-artifacts.md --- docs/guides/install/azure-artifacts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index bc2e777f6..8094d6f4f 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -6,7 +6,7 @@ name: Using bun install with an Azure Artifacts npm registry To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents: -### Configure with bunfig.toml +**Configure with bunfig.toml** Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. @@ -21,7 +21,7 @@ password = "$NPM_PASSWORD" Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. -### Configure with environment variables +**Configure with environment variables** You can also use an environment variable to configure Azure Artifacts with bun install. -- cgit v1.2.3 From 346f8e9c944920d910535ad98c10ed6381621cc7 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:52:42 -0700 Subject: Update azure-artifacts.md --- docs/guides/install/azure-artifacts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index 8094d6f4f..5f52b942c 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -38,7 +38,7 @@ Make sure to: - Replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234` - Replace `my-azure-artifacts-password` with the non-base64 encoded password for your Azure Artifacts npm registry -### Don't base64 encode the password +**Don't base64 encode the password** In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. -- cgit v1.2.3 From d2328285f952537603fa2cbcdef44fde50c69b82 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Tue, 19 Sep 2023 00:13:52 -0700 Subject: Update azure-artifacts.md --- docs/guides/install/azure-artifacts.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index 5f52b942c..d173c1f81 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -6,7 +6,9 @@ name: Using bun install with an Azure Artifacts npm registry To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents: -**Configure with bunfig.toml** +--- + +### Configure with bunfig.toml Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. @@ -21,7 +23,9 @@ password = "$NPM_PASSWORD" Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. -**Configure with environment variables** +--- + +### Configure with environment variables You can also use an environment variable to configure Azure Artifacts with bun install. @@ -38,7 +42,9 @@ Make sure to: - Replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234` - Replace `my-azure-artifacts-password` with the non-base64 encoded password for your Azure Artifacts npm registry -**Don't base64 encode the password** +--- + +### Don't base64 encode the password In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. -- cgit v1.2.3 From 8677ae9fb154dea49939dd396fdd1363959f96de Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 19 Sep 2023 08:01:47 -0700 Subject: Get artifactory to work (#5744) * Get artifactory to work * Cleanup url normalization a ltitle more * Clean up tests * prettier --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- docs/guides/install/jfrog-artifactory.md | 28 ++++++++++ src/bunfig.zig | 93 +++++++++++++++++--------------- src/install/install.zig | 12 ++--- src/install/npm.zig | 56 ++++++++++++++++++- test/cli/install/bun-add.test.ts | 22 ++++---- test/cli/install/bun-install.test.ts | 21 ++++---- 6 files changed, 163 insertions(+), 69 deletions(-) create mode 100644 docs/guides/install/jfrog-artifactory.md (limited to 'docs/guides/install') diff --git a/docs/guides/install/jfrog-artifactory.md b/docs/guides/install/jfrog-artifactory.md new file mode 100644 index 000000000..e4872982b --- /dev/null +++ b/docs/guides/install/jfrog-artifactory.md @@ -0,0 +1,28 @@ +--- +name: Using bun install with Artifactory +--- + +[JFrog Artifactory](https://jfrog.com/artifactory/) is a package management system for npm, Docker, Maven, NuGet, Ruby, Helm, and more. It allows you to host your own private npm registry, npm packages, and other types of packages as well. + +To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents: + +--- + +### Configure with bunfig.toml + +Make sure to replace `MY_SUBDOMAIN` with your JFrog Artifactory subdomain, such as `jarred1234` and MY_TOKEN with your JFrog Artifactory token. + +```toml#bunfig.toml +[install.registry] +url = "https://MY_SUBDOMAIN.jfrog.io/artifactory/api/npm/npm/_auth=MY_TOKEN" +# Bun v1.0.3+ supports using an environment variable here +# url = "$NPM_CONFIG_REGISTRY" +``` + +--- + +### Configure with `$NPM_CONFIG_REGISTRY` + +Like with npm, you can use the `NPM_CONFIG_REGISTRY` environment variable to configure JFrog Artifactory with bun install. + +--- diff --git a/src/bunfig.zig b/src/bunfig.zig index bb52e3053..63a6c389d 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -55,59 +55,68 @@ pub const Bunfig = struct { return error.@"Invalid Bunfig"; } - fn parseRegistry(this: *Parser, expr: js_ast.Expr) !Api.NpmRegistry { + fn parseRegistryURLString(this: *Parser, str: *js_ast.E.String) !Api.NpmRegistry { + const url = URL.parse(str.data); + var registry = std.mem.zeroes(Api.NpmRegistry); + + // Token + if (url.username.len == 0 and url.password.len > 0) { + registry.token = url.password; + registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{}/{s}/", .{ url.displayProtocol(), url.displayHost(), std.mem.trim(u8, url.pathname, "/") }); + } else if (url.username.len > 0 and url.password.len > 0) { + registry.username = url.username; + registry.password = url.password; + + registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{}/{s}/", .{ url.displayProtocol(), url.displayHost(), std.mem.trim(u8, url.pathname, "/") }); + } else { + // Do not include a trailing slash. There might be parameters at the end. + registry.url = url.href; + } + + return registry; + } + + fn parseRegistryObject(this: *Parser, obj: *js_ast.E.Object) !Api.NpmRegistry { var registry = std.mem.zeroes(Api.NpmRegistry); + if (obj.get("url")) |url| { + try this.expect(url, .e_string); + const href = url.data.e_string.data; + // Do not include a trailing slash. There might be parameters at the end. + registry.url = href; + } + + if (obj.get("username")) |username| { + try this.expect(username, .e_string); + registry.username = username.data.e_string.data; + } + + if (obj.get("password")) |password| { + try this.expect(password, .e_string); + registry.password = password.data.e_string.data; + } + + if (obj.get("token")) |token| { + try this.expect(token, .e_string); + registry.token = token.data.e_string.data; + } + + return registry; + } + + fn parseRegistry(this: *Parser, expr: js_ast.Expr) !Api.NpmRegistry { switch (expr.data) { .e_string => |str| { - const url = URL.parse(str.data); - // Token - if (url.username.len == 0 and url.password.len > 0) { - registry.token = url.password; - registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{s}/{s}/", .{ url.displayProtocol(), url.displayHostname(), std.mem.trim(u8, url.pathname, "/") }); - } else if (url.username.len > 0 and url.password.len > 0) { - registry.username = url.username; - registry.password = url.password; - registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{s}/{s}/", .{ url.displayProtocol(), url.displayHostname(), std.mem.trim(u8, url.pathname, "/") }); - } else { - if (strings.hasSuffixComptime(url.href, "/")) { - registry.url = url.href; - } else { - registry.url = try std.fmt.allocPrint(this.allocator, "{s}/", .{url.href}); - } - } + return this.parseRegistryURLString(str); }, .e_object => |obj| { - if (obj.get("url")) |url| { - try this.expect(url, .e_string); - if (strings.hasSuffixComptime(url.data.e_string.data, "/")) { - registry.url = url.data.e_string.data; - } else { - registry.url = try std.fmt.allocPrint(this.allocator, "{s}/", .{url.data.e_string.data}); - } - } - - if (obj.get("username")) |username| { - try this.expect(username, .e_string); - registry.username = username.data.e_string.data; - } - - if (obj.get("password")) |password| { - try this.expect(password, .e_string); - registry.password = password.data.e_string.data; - } - - if (obj.get("token")) |token| { - try this.expect(token, .e_string); - registry.token = token.data.e_string.data; - } + return this.parseRegistryObject(obj); }, else => { try this.addError(expr.loc, "Expected registry to be a URL string or an object"); + return std.mem.zeroes(Api.NpmRegistry); }, } - - return registry; } fn loadLogLevel(this: *Parser, expr: js_ast.Expr) !void { diff --git a/src/install/install.zig b/src/install/install.zig index c7a2816a5..3757a6980 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -273,8 +273,8 @@ const NetworkTask = struct { if (tmp.tag == .Dead) { const msg = .{ - .fmt = "Failed to join registry \"{s}\" and package \"{s}\" URLs", - .args = .{ scope.url.href, name }, + .fmt = "Failed to join registry {} and package {} URLs", + .args = .{ strings.QuotedFormatter{ .text = scope.url.href }, strings.QuotedFormatter{ .text = name } }, }; if (warn_on_error) @@ -3806,10 +3806,10 @@ pub const PackageManager = struct { switch (response.status_code) { 404 => { if (comptime log_level != .silent) { - const fmt = "\nerror: package \"{s}\" not found {s}{s} 404\n"; + const fmt = "\nerror: package \"{s}\" not found {}{s} 404\n"; const args = .{ name.slice(), - task.http.url.displayHostname(), + task.http.url.displayHost(), task.http.url.pathname, }; @@ -3823,10 +3823,10 @@ pub const PackageManager = struct { }, 401 => { if (comptime log_level != .silent) { - const fmt = "\nerror: unauthorized \"{s}\" {s}{s} 401\n"; + const fmt = "\nerror: unauthorized \"{s}\" {}{s} 401\n"; const args = .{ name.slice(), - task.http.url.displayHostname(), + task.http.url.displayHost(), task.http.url.pathname, }; diff --git a/src/install/npm.zig b/src/install/npm.zig index fec545b0c..24e631836 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -81,6 +81,7 @@ pub const Registry = struct { var url = URL.parse(registry.url); var auth: string = ""; + var needs_normalize = false; if (registry.token.len == 0) { outer: { @@ -90,10 +91,12 @@ pub const Registry = struct { url.pathname = pathname; url.path = pathname; } - + var needs_to_check_slash = true; while (strings.lastIndexOfChar(pathname, ':')) |colon| { var segment = pathname[colon + 1 ..]; pathname = pathname[0..colon]; + needs_to_check_slash = false; + needs_normalize = true; if (pathname.len > 1 and pathname[pathname.len - 1] == '/') { pathname = pathname[0 .. pathname.len - 1]; } @@ -124,6 +127,47 @@ pub const Registry = struct { continue; } } + + // In this case, there is only one. + if (needs_to_check_slash) { + if (strings.lastIndexOfChar(pathname, '/')) |last_slash| { + var remain = pathname[last_slash + 1 ..]; + if (strings.indexOfChar(remain, '=')) |eql_i| { + const segment = remain[0..eql_i]; + var value = remain[eql_i + 1 ..]; + + // https://github.com/yarnpkg/yarn/blob/6db39cf0ff684ce4e7de29669046afb8103fce3d/src/registries/npm-registry.js#L364 + // Bearer Token + if (strings.eqlComptime(segment, "_authToken")) { + registry.token = value; + pathname = pathname[0 .. last_slash + 1]; + needs_normalize = true; + break :outer; + } + + if (strings.eqlComptime(segment, "_auth")) { + auth = value; + pathname = pathname[0 .. last_slash + 1]; + needs_normalize = true; + break :outer; + } + + if (strings.eqlComptime(segment, "username")) { + registry.username = value; + pathname = pathname[0 .. last_slash + 1]; + needs_normalize = true; + break :outer; + } + + if (strings.eqlComptime(segment, "_password")) { + registry.password = value; + pathname = pathname[0 .. last_slash + 1]; + needs_normalize = true; + break :outer; + } + } + } + } } registry.username = env.getAuto(registry.username); @@ -144,6 +188,16 @@ pub const Registry = struct { registry.token = env.getAuto(registry.token); + if (needs_normalize) { + url = URL.parse( + try std.fmt.allocPrint(allocator, "{s}://{}/{s}/", .{ + url.displayProtocol(), + url.displayHost(), + strings.trim(url.pathname, "/"), + }), + ); + } + return Scope{ .name = name, .url = url, .token = registry.token, .auth = auth }; } }; diff --git a/test/cli/install/bun-add.test.ts b/test/cli/install/bun-add.test.ts index c00c0ba7b..14ae44f73 100644 --- a/test/cli/install/bun-add.test.ts +++ b/test/cli/install/bun-add.test.ts @@ -20,7 +20,11 @@ import { beforeAll(dummyBeforeAll); afterAll(dummyAfterAll); +let port: string; let add_dir: string; +beforeAll(() => { + port = new URL(root_url).port; +}); beforeEach(async () => { add_dir = await mkdtemp(join(await realpath(tmpdir()), "bun-add.test")); @@ -102,9 +106,9 @@ it("should reject missing package", async () => { }); expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.includes("bun add")).toBeTrue(); - expect(err.includes("error: MissingPackageJSON")).toBeTrue(); - expect(err.includes(`note: error occured while resolving file:${add_path}`)).toBeTrue(); + expect(err).toContain("bun add"); + expect(err).toContain("error: MissingPackageJSON"); + expect(err).toContain(`note: error occured while resolving file:${add_path}`); expect(stdout).toBeDefined(); const out = await new Response(stdout).text(); @@ -144,9 +148,9 @@ it("should reject invalid path without segfault", async () => { }); expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.includes("bun add")).toBeTrue(); - expect(err.includes("error: MissingPackageJSON")).toBeTrue(); - expect(err.includes(`note: error occured while resolving file://${add_path}`)).toBeTrue(); + expect(err).toContain("bun add"); + expect(err).toContain("error: MissingPackageJSON"); + expect(err).toContain(`note: error occured while resolving file://${add_path}`); expect(stdout).toBeDefined(); const out = await new Response(stdout).text(); @@ -189,7 +193,7 @@ it("should handle semver-like names", async () => { }); expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.split(/\r?\n/)).toContain('error: package "1.2.3" not found localhost/1.2.3 404'); + expect(err.split(/\r?\n/)).toContain(`error: package "1.2.3" not found localhost:${port}/1.2.3 404`); expect(stdout).toBeDefined(); expect(await new Response(stdout).text()).toBe(""); expect(await exited).toBe(1); @@ -232,7 +236,7 @@ it("should handle @scoped names", async () => { }); expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.split(/\r?\n/)).toContain('error: package "@bar/baz" not found localhost/@bar%2fbaz 404'); + expect(err.split(/\r?\n/)).toContain(`error: package "@bar/baz" not found localhost:${port}/@bar%2fbaz 404`); expect(stdout).toBeDefined(); expect(await new Response(stdout).text()).toBe(""); expect(await exited).toBe(1); @@ -1512,7 +1516,7 @@ async function installRedirectsToAdd(saveFlagFirst: boolean) { " 1 packages installed", ]); expect(await exited).toBe(0); - expect((await file(join(package_dir, "package.json")).text()).includes("bun-add.test")); + expect(await file(join(package_dir, "package.json")).text()).toInclude("bun-add.test"); } it("should add dependency alongside peerDependencies", async () => { diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index 25cd0aca7..354909745 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -96,7 +96,7 @@ it("should handle missing package", async () => { }); expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.split(/\r?\n/)).toContain('error: package "foo" not found localhost/foo 404'); + expect(err.split(/\r?\n/)).toContain(`error: package "foo" not found localhost:${new URL(root_url).port}/foo 404`); expect(stdout).toBeDefined(); expect(await new Response(stdout).text()).toBeEmpty(); expect(await exited).toBe(1); @@ -6022,13 +6022,13 @@ describe("Registry URLs", () => { ["https://registry.npmjs.org/", false], ["https://artifactory.xxx.yyy/artifactory/api/npm/my-npm/", false], // https://github.com/oven-sh/bun/issues/3899 ["https://artifactory.xxx.yyy/artifactory/api/npm/my-npm", false], // https://github.com/oven-sh/bun/issues/5368 - ["", true], + // ["", true], ["https:example.org", false], ["https://////example.com///", false], ["https://example.com/https:example.org", false], ["https://example.com/[]?[]#[]", false], ["https://example/%?%#%", false], - ["c:", false], + ["c:", true], ["c:/", false], ["https://點看", false], // gets converted to punycode ["https://xn--c1yn36f/", false], @@ -6067,11 +6067,10 @@ describe("Registry URLs", () => { const err = await new Response(stderr).text(); if (fails) { - const url = regURL.at(-1) === "/" ? regURL : regURL + "/"; - expect(err.includes(`Failed to join registry \"${url}\" and package \"notapackage\" URLs`)).toBeTrue(); - expect(err.includes("error: InvalidURL")).toBeTrue(); + expect(err).toContain(`Failed to join registry "${regURL}" and package "notapackage" URLs`); + expect(err).toContain("error: InvalidURL"); } else { - expect(err.includes("error: notapackage@0.0.2 failed to resolve")).toBeTrue(); + expect(err).toContain("error: notapackage@0.0.2 failed to resolve"); } // fails either way, since notapackage is, well, not a real package. expect(await exited).not.toBe(0); @@ -6108,8 +6107,8 @@ describe("Registry URLs", () => { expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.includes(`Failed to join registry \"${regURL}/\" and package \"notapackage\" URLs`)).toBeTrue(); - expect(err.includes("warn: InvalidURL")).toBeTrue(); + expect(err).toContain(`Failed to join registry "${regURL}" and package "notapackage" URLs`); + expect(err).toContain("warn: InvalidURL"); expect(await exited).toBe(0); }); @@ -6149,8 +6148,8 @@ describe("Registry URLs", () => { expect(stderr).toBeDefined(); const err = await new Response(stderr).text(); - expect(err.includes(`Failed to join registry \"${regURL}\" and package \"notapackage\" URLs`)).toBeTrue(); - expect(err.includes("warn: InvalidURL")).toBeTrue(); + expect(err).toContain(`Failed to join registry "${regURL}" and package "notapackage" URLs`); + expect(err).toContain("warn: InvalidURL"); expect(await exited).toBe(0); }); -- cgit v1.2.3 From ebc7b037ed6e44cd48e0771e446c2cbb5130d888 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Tue, 19 Sep 2023 12:22:09 -0700 Subject: Update azure guide --- docs/guides/install/azure-artifacts.md | 40 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index d173c1f81..b1b3cae1d 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -2,6 +2,10 @@ name: Using bun install with an Azure Artifacts npm registry --- +{% callout %} +In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. +{% /callout %} + [Azure Artifacts](https://azure.microsoft.com/en-us/products/devops/artifacts) is a package management system for Azure DevOps. It allows you to host your own private npm registry, npm packages, and other types of packages as well. To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents: @@ -12,6 +16,8 @@ To use it with `bun install`, add a `bunfig.toml` file to your project with the Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. +Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. + ```toml#bunfig.toml [install.registry] url = "https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry" @@ -20,44 +26,42 @@ username = "my-azure-artifacts-user" password = "$NPM_PASSWORD" ``` -Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. - - --- ### Configure with environment variables -You can also use an environment variable to configure Azure Artifacts with bun install. +You can also use an environment variable to configure Azure Artifacts with `bun install`. -Like with the `npm` CLI, the environment variable to use is `NPM_CONFIG_REGISTRY`. - -The URL should include `:username` and `:_password` as query parameters. For example: +Like with the `npm` CLI, the environment variable to use is `NPM_CONFIG_REGISTRY`. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the apprropriate values. ```bash#shell -NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=my-azure-artifacts-user:_password=my-azure-artifacts-password +NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=:_password= ``` -Make sure to: - -- Replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234` -- Replace `my-azure-artifacts-password` with the non-base64 encoded password for your Azure Artifacts npm registry - --- ### Don't base64 encode the password +--- + In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. -To un-base64 encode a password, you can open your browser console and run: +{% callout %} +**Tip** — If it ends with `==`, it probably is base64 encoded. +{% /callout %} + +--- + +To decode a base64-encoded password, open your browser console and run: ```js -atob("base64-encoded-password"); +atob(""); ``` -You can also use the `base64` command line tool, but doing so means it may be saved in your terminal history which is not recommended: +--- + +Alternatively, use the `base64` command line tool, but doing so means it may be saved in your terminal history which is not recommended: ```bash echo "base64-encoded-password" | base64 --decode ``` - -If it ends with `==`, it probably is base64 encoded. \ No newline at end of file -- cgit v1.2.3 From ef98a1b76167069e360291b9901ece985c6fa263 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Tue, 19 Sep 2023 13:38:00 -0700 Subject: Improve formatting again --- docs/guides/install/azure-artifacts.md | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index b1b3cae1d..a317dff43 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -14,6 +14,8 @@ To use it with `bun install`, add a `bunfig.toml` file to your project with the ### Configure with bunfig.toml +--- + Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. @@ -30,6 +32,8 @@ password = "$NPM_PASSWORD" ### Configure with environment variables +--- + You can also use an environment variable to configure Azure Artifacts with `bun install`. Like with the `npm` CLI, the environment variable to use is `NPM_CONFIG_REGISTRY`. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the apprropriate values. -- cgit v1.2.3 From 615beee1ae0fd9b299fc38ac0989d4be5899c19b Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Tue, 19 Sep 2023 13:48:59 -0700 Subject: More improvements to azure guide --- docs/guides/install/azure-artifacts.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.md index a317dff43..659e75fd6 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.md @@ -8,17 +8,13 @@ In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/n [Azure Artifacts](https://azure.microsoft.com/en-us/products/devops/artifacts) is a package management system for Azure DevOps. It allows you to host your own private npm registry, npm packages, and other types of packages as well. -To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents: - --- ### Configure with bunfig.toml --- -Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. - -Set the `$NPM_PASSWORD` environment variable to your Azure Artifacts npm registry password and Bun will automatically replace it with the correct value. You can also choose not to use an environment variable and instead hardcode your password in the `bunfig.toml` file, but be careful not to commit it to source control. +To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents. Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. ```toml#bunfig.toml [install.registry] @@ -30,13 +26,19 @@ password = "$NPM_PASSWORD" --- -### Configure with environment variables +Then assign your Azure Personal Access Token to the the `NPM_PASSWORD` environment variable. Bun [automatically reads](/docs/runtime/env) `.env` files, so create a file called `.env` in your project root. There is no need to base-64 encode this token! Bun will do this for you. + +```txt#.env +NPM_PASSWORD= +``` --- -You can also use an environment variable to configure Azure Artifacts with `bun install`. +### Configure with environment variables + +--- -Like with the `npm` CLI, the environment variable to use is `NPM_CONFIG_REGISTRY`. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the apprropriate values. +To configure Azure Artifacts without `bunfig.toml`, you can set the `NPM_CONFIG_REGISTRY` environment variable. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the apprropriate values. ```bash#shell NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=:_password= -- cgit v1.2.3 From e2fb524993137bce0a6d3a2a45783877efff4f82 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Tue, 19 Sep 2023 22:42:54 -0700 Subject: Fix broken links --- docs/guides/install/custom-registry.md | 2 +- docs/guides/install/registry-scope.md | 2 +- docs/guides/runtime/read-env.md | 2 +- docs/guides/runtime/set-env.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/custom-registry.md b/docs/guides/install/custom-registry.md index 64b3cf76b..12bb3b77f 100644 --- a/docs/guides/install/custom-registry.md +++ b/docs/guides/install/custom-registry.md @@ -18,7 +18,7 @@ registry = "https://username:password@registry.npmjs.org" --- -Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/docs/cli/run#environment-variables) for more information. +Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/docs/runtime/env) for more information. ```toml#bunfig.toml [install] diff --git a/docs/guides/install/registry-scope.md b/docs/guides/install/registry-scope.md index 68d8cf942..aade23116 100644 --- a/docs/guides/install/registry-scope.md +++ b/docs/guides/install/registry-scope.md @@ -24,7 +24,7 @@ Bun does not read `.npmrc` files; instead private registries are configured via --- -Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/docs/cli/run#environment-variables) for more information. +Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/docs/runtime/env) for more information. ```toml#bunfig.toml [install.scopes] diff --git a/docs/guides/runtime/read-env.md b/docs/guides/runtime/read-env.md index 512f731dd..7295a6726 100644 --- a/docs/guides/runtime/read-env.md +++ b/docs/guides/runtime/read-env.md @@ -29,4 +29,4 @@ FOOBAR=aaaaaa --- -See [Docs > Runtime > Environment variables](/docs/cli/run#environment-variables) for more information on using environment variables with Bun. +See [Docs > Runtime > Environment variables](/docs/runtime/env) for more information on using environment variables with Bun. diff --git a/docs/guides/runtime/set-env.md b/docs/guides/runtime/set-env.md index 97cac3488..684da940a 100644 --- a/docs/guides/runtime/set-env.md +++ b/docs/guides/runtime/set-env.md @@ -34,4 +34,4 @@ $ FOO=helloworld bun run dev --- -See [Docs > Runtime > Environment variables](/docs/cli/run#environment-variables) for more information on using environment variables with Bun. +See [Docs > Runtime > Environment variables](/docs/runtime/env) for more information on using environment variables with Bun. -- cgit v1.2.3 From 3f2df4526ed876869a14aaf9d373328fc4dce94e Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Thu, 12 Oct 2023 23:17:51 -0700 Subject: Fix links --- docs/guides/install/trusted.md | 2 +- docs/install/overrides.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/guides/install') diff --git a/docs/guides/install/trusted.md b/docs/guides/install/trusted.md index d0d841eea..0c1ac6362 100644 --- a/docs/guides/install/trusted.md +++ b/docs/guides/install/trusted.md @@ -47,4 +47,4 @@ Note that this only allows lifecycle scripts for the specific package listed in --- -See [Docs > Package manager > Trusted dependencies](/docs/cli/install#trusted-dependencies) for complete documentation of trusted dependencies. +See [Docs > Package manager > Trusted dependencies](/docs/install/lifecycle) for complete documentation of trusted dependencies. diff --git a/docs/install/overrides.md b/docs/install/overrides.md index 27f5a92b2..f226c35bd 100644 --- a/docs/install/overrides.md +++ b/docs/install/overrides.md @@ -1,4 +1,4 @@ -Bun supports npm's `"overrides"` and Yarn's `"resolutions"` in `package.json`. These are mechanisms for specifying a version range for _metadependencies_—the dependencies of your dependencies. Refer to [Package manager > Overrides and resolutions](/docs/install/overrides-and-resolutions) for complete documentation. +Bun supports npm's `"overrides"` and Yarn's `"resolutions"` in `package.json`. These are mechanisms for specifying a version range for _metadependencies_—the dependencies of your dependencies. Refer to [Package manager > Overrides and resolutions](/docs/install/overrides) for complete documentation. ```json-diff#package.json { -- cgit v1.2.3