Skip to content

Commit

Permalink
✨feat(minor): browserslist auto upgrade (#2493)
Browse files Browse the repository at this point in the history
Auto upgrade browserslist before executing build.

Can be disabled with `--no-update-browserslist` flag.

## Changes

- Add: `--update-browserslist` flag for `bud build` commands
- Docs: Improve documentation for .env files
- Test: Add additional env and argv tests

## Type of change

**PATCH: backwards compatible change**
  • Loading branch information
kellymears committed Nov 15, 2023
1 parent 4216c19 commit 347ec8a
Show file tree
Hide file tree
Showing 44 changed files with 722 additions and 198 deletions.
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# All bud installs in the repository will find this env file

# Easier to debug if cache is local to each project
APP_STORAGE_PATH=.storage
BUD_PATH_STORAGE=.storage
BUD_UPDATE_BROWSERSLIST=false
3 changes: 3 additions & 0 deletions examples/sage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"$schema": "https://bud.js.org/bud.package.json",
"private": true,
"type": "module",
"browserslist": [
"extends @roots/browserslist-config"
],
"devDependencies": {
"@roots/bud": "workspace:*",
"@roots/bud-tailwindcss": "workspace:*",
Expand Down
9 changes: 9 additions & 0 deletions examples/tailwindcss/colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default {
blue: {
100: '#ebf8ff',
200: '#bee3f8',
300: '#90cdf4',
400: '#63b3ed',
500: '#4299e1',
}
}
13 changes: 7 additions & 6 deletions examples/tailwindcss/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
export default {
content: ['src/**/*.{js,css,html}'],
import colors from './colors.js'

const config = {
content: ['./src/**/*.js'],
theme: {
extend: {
colors: {},
colors, // Extend Tailwind's default colors
},
},
variants: {
extend: {},
},
plugins: [],
}

export default config
74 changes: 74 additions & 0 deletions sources/@repo/docs/content/learn/config/files/env.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,77 @@ sidebar_label: .env
---

You can access environment variables set in a `.env` file in your project root using [bud.env](/reference/bud.env).

## Precedence

Environment variables are sourced from `.env` files recursively, starting with the project root directory. The closer the file is to the project root, the higher priority it has.

So, if you have an `.env` file in your project root and another in the parent directory, and both contain a value for the same key, the value from the project root will be used.

## Expansion

bud.js supports expansion of environment variables. For example:

```bash
PUBLIC_API_ORIGIN=https://api.example.com
PUBLIC_API_URL=${PUBLIC_API_URL}/endpoint
```

## Public variables

Any environment variables that are prefixed with `PUBLIC_` will be available to your application code. When using variable values in your application you
must remove the `PUBLIC_` prefix.

For example, setting `PUBLIC_API_URL`:

```bash
PUBLIC_API_URL=https://api.example.com
```

Means `API_URL` can be used in your application code:

```js
const request = fetch(`${API_URL}/endpoint`)
```

The replacement is static and happens at build time. It is not the same as defining a global.

For example, attempting to redefine it will cause a type error:

```js
/**
* Don't do this
*/
API_URL = 'https://api.example.com'
```

## Configurable environment variables

| Variable | Description | Related argument |
| ------------------------- | ----------------------------------------------------------------------------------- | ----------------------- |
| `BUD_CACHE` | Enable or disable bud.js caching | `--cache` |
| `BUD_DEVTOOL` | The devtool to use for builds | `--devtool` |
| `BUD_ESM` | Enable or disable bud.js ESM output (experimental) | `--esm` |
| `BUD_HASH` | Enable or disable output file hashing | `--hash` |
| `BUD_HTML` | Enable HTML templating; set to a string to specify the path to a template | `--html` |
| `BUD_HOT` | Enable or disable bud.js hot reloading | `--hot` |
| `BUD_IMMUTABLE` | Enable or disable automated updates of dependencies when using remote modules | `--immutable` |
| `BUD_LAZY` | Enable or disable lazy compilation of modules in development | `--lazy` |
| `BUD_MINIMIZE` | Enable or disable code minimization | `--minimize` |
| `BUD_PATH_BASE` | The path to the root directory of the bud.js project | `--basedir` |
| `BUD_PATH_STORAGE` | The path to the directory where bud.js stores its data and caches | `--storage` |
| `BUD_PATH_INPUT` | The path to the directory where bud.js looks for source files | `--input` |
| `BUD_PATH_OUTPUT` | The path to the directory where bud.js outputs build files | `--output` |
| `BUD_PATH_PUBLIC` | The path to the directory where bud.js outputs public files | `--publicPath` |
| `BUD_PROXY_URL` | The URL of the proxy server to use | `--proxy` |
| `BUD_RUNTIME` | Enable or disable generation of a runtime module | `--runtime` |
| `BUD_SPLIT_CHUNKS` | Enable or disable splitting of modules into discrete chunks (`vendor`, `app`, etc.) | `--split-chunks` |
| `BUD_UPDATE_BROWSERSLIST` | Set to `false` in order to disable automatic updating of the browserslist database. | `--update-browserslist` |

## CLI

You can verify what environment variables are available to bud.js using the `bud env` command:

```bash npm2yarn
npm run bud env
```
1 change: 1 addition & 0 deletions sources/@repo/test-kit/integration-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class Project {
`--basedir`,
this.options.label.replace(`@examples/`, `examples/`),
`build`,
`--no-update-browserslist`,
])
}

Expand Down
6 changes: 6 additions & 0 deletions sources/@roots/bud-api/docs/define/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ Replacements will also be made when compiling [html templates](/reference/bud.ht
<!-- ... -->
</html>
```

## Caveats

This replacement is static and happens at build time. It is not the same as a runtime variable.

Doing things like attempting to reassign its value in your application code will not work.
6 changes: 3 additions & 3 deletions sources/@roots/bud-api/src/methods/splitChunks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export interface splitChunks {
*/
export const splitChunks: splitChunks = async function (this: Bud, value) {
/**
* For `true` and `undefined` options the default
* For true and undefined options the default
* cache groups are added to the build
*/
if (isUndefined(value) || value === true) {
Expand All @@ -54,9 +54,9 @@ export const splitChunks: splitChunks = async function (this: Bud, value) {

return {
automaticNameDelimiter: sep,
...options,
...(options ?? {}),
cacheGroups: {
...(options.cacheGroups ?? {}),
...(options?.cacheGroups ?? {}),
default: false,
vendor: {
chunks: `all`,
Expand Down
19 changes: 11 additions & 8 deletions sources/@roots/bud-api/test/define.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@ describe(`bud.define`, function () {
it(`adds definitions`, () => {
define({DEFINED_KEY: `DEFINED_VALUE`})

expect(
bud.extensions.get(`@roots/bud-extensions/webpack-define-plugin`)
.options.DEFINED_KEY,
).toEqual(`DEFINED_VALUE`)
const {options} = bud.extensions.get(
`@roots/bud-extensions/webpack-define-plugin`,
)

expect(options.DEFINED_KEY).toEqual(`DEFINED_VALUE`)
})

it(`adds PUBLIC_APP_TITLE from env`, async () => {
await bud.run()
expect(
bud.extensions.get(`@roots/bud-extensions/webpack-define-plugin`)
.options.APP_TITLE,
).toBeDefined()

const {options} = bud.extensions.get(
`@roots/bud-extensions/webpack-define-plugin`,
)

expect(options.APP_TITLE).toBeDefined()
})

it(`matches snapshot`, () => {
Expand Down
17 changes: 17 additions & 0 deletions sources/@roots/bud-framework/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {Bud} from '@roots/bud-framework'
import type {PackageManager} from '@roots/bud-support/which-pm'

import type {parse} from 'node:path'

Expand Down Expand Up @@ -402,6 +403,14 @@ export interface Context {
storage: string
}

/**
* Package manager option
*
* @remarks
* Set with the `--pm` CLI flag.
*/
pm?: PackageManager

/**
* Port option
*
Expand Down Expand Up @@ -506,6 +515,14 @@ export interface Context {
*/
target?: Array<string>

/**
* Update browserslist option
*
* @remarks
* Set with the `--update-browserslist-check` CLI flag.
*/
updateBrowserslistCheck?: boolean

/**
* Use option
*
Expand Down
14 changes: 13 additions & 1 deletion sources/@roots/bud-framework/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ export default class Env extends ServiceContainer {
*/
@bind
public override async bootstrap(bud: Bud) {
this.setStore(bud.context.env)
const records = bud.context?.env ?? {}
const coerce = (a: Record<string, any>, [k, v]) => {
switch (v) {
case `true`:
return {...a, [k]: true}
case `false`:
return {...a, [k]: false}
default:
return {...a, [k]: v}
}
}

this.setStore(Object.entries(records).reduce(coerce, {}))
}

/**
Expand Down
6 changes: 3 additions & 3 deletions sources/@roots/bud-framework/src/methods/when.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export interface when {
}

/**
* Executes a function if a given test is `true`.
* Executes a function if a given test is true
*
* @remarks
* - The first parameter is the conditional check.
* - The second parameter is the function to run if `true`.
* - The third parameter is optional; executed if the conditional is not `true`.
* - The second parameter is the function to run if true.
* - The third parameter is optional; executed if the conditional is not true.
*
* @example
* Only produce a vendor bundle when running in `production`:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import stripAnsi from '@roots/bud-support/strip-ansi'
import {execa} from 'execa'
import {describe, expect, it} from 'vitest'
import stripAnsi from '@roots/bud-support/strip-ansi'

describe(`bud build with extensionless stylelintrc`, () => {
it(`should build with expected stdout`, async () => {
Expand All @@ -10,9 +10,11 @@ describe(`bud build with extensionless stylelintrc`, () => {
`run`,
`bud`,
`build`,
`--no-update-browserslist`,
])

const [_s, title, _s2, entry, runtime, css, js, _s3, timings] = stripAnsi(result.stdout).split(`\n`)
const [_s, title, _s2, entry, runtime, css, js, _s3, timings] =
stripAnsi(result.stdout).split(`\n`)

expect(title).toMatch(/╭ stylelintrc-no-extension \[.*\].*\.\/dist/)
expect(entry).toMatch(/│ app/)
Expand Down
19 changes: 10 additions & 9 deletions sources/@roots/bud-support/src/utilities/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ This is most likely a problem with the internals of bud.js.`,
let hash: string
let env = envBootstrap.get(basedir)

const specified = args.basedir ?? env.APP_BASE_PATH
const specified = args.basedir ?? env.BUD_PATH_BASE ?? env.APP_BASE_PATH

if (specified && !basedir.endsWith(specified)) {
logger.scope(`paths`).log(`using specified basedir:`, specified)

Expand All @@ -89,14 +90,14 @@ This is most likely a problem with the internals of bud.js.`,
}

hash = sha1.digest(`base64url`)
let storage: string = join(systemPaths.cache, hash)

if (args.storage || args[`@storage`] || env.APP_STORAGE_PATH) {
storage = join(
basedir,
args.storage ?? args[`@storage`] ?? env.APP_STORAGE_PATH,
)
}
let storage: string =
args.storage ??
args[`@storage`] ??
env.BUD_PATH_STORAGE ??
env.APP_STORAGE_PATH ??
join(systemPaths.cache, hash)

storage = join(basedir, storage)

paths = {
...Object.entries(systemPaths).reduce(
Expand Down
23 changes: 16 additions & 7 deletions sources/@roots/bud-support/src/which-pm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import * as file from './file.js'
import * as manifest from './manifest.js'
import * as pmString from './pmString.js'

export default async function (
basedir: string = cwd,
): Promise<`npm` | `pnpm` | `yarn-classic` | `yarn` | false> {
export type PackageManager =
| `bun`
| `npm`
| `pnpm`
| `yarn-classic`
| `yarn`
export type Responses = false | PackageManager

export default async function (basedir: string = cwd): Promise<Responses> {
/**
* If set, it will be something like: `npm/7.20.3 node/v14.17.3 darwin x64`
*/
Expand All @@ -25,17 +31,20 @@ export default async function (
if (manager) return manager
}

/** This config file is only present in bun projects */
if (await file.exists(basedir, `bun.lockb`)) return `bun`

/** This config file is only present in pnpm projects */
if (await file.exists(basedir, `pnpm-lock.yaml`)) return `pnpm`

/** This config file is only present in Yarn 3 projects. */
if (await file.exists(basedir, `.yarnrc.yml`)) return `yarn`

/** If there is a `yarn.lock` file and no `.yarnrc.yml`, it's a Yarn Classic project. */
/** If there is a `yarn.lock` file it's a Yarn Classic project. */
if (await file.exists(basedir, `yarn.lock`)) return `yarn-classic`

/** If there is a `package-lock.json` file, it's an npm project. */
if (await file.exists(basedir, `package-lock.json`)) return `npm`

/** If there is a `pnpm-lock.yaml` file, it's a pnpm project. */
if (await file.exists(basedir, `pnpm-lock.yaml`)) return `pnpm`

return false
}
5 changes: 4 additions & 1 deletion sources/@roots/bud-support/src/which-pm/pmString.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { Responses } from "./index.js"

export const parse = (
pmString?: string,
): `npm` | `pnpm` | `yarn-classic` | `yarn` | false => {
): Responses => {
if (!pmString) return false

if (pmString.match(/yarn(\/|@)(3|4).*/)) return `yarn`
if (pmString.includes(`yarn`)) return `yarn-classic`
if (pmString.includes(`bun`)) return `bun`
if (pmString.includes(`npm`)) return `npm`
if (pmString.includes(`pnpm`)) return `pnpm`

Expand Down
Loading

0 comments on commit 347ec8a

Please sign in to comment.