Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
Bot releases are visible (Hide)
Published by Jarred-Sumner over 2 years ago
To upgrade:
bun upgrade
# Try this
curl https://bun.sh/install | bash
Please let me know though if it doesn't work and you're on bun v0.0.69. bun v0.0.61 - v0.0.68 had issues with the HTTP client
bun install
: numerous bug fixes to linking, serialization, deserialization, extracting files. It probably works in many projects now. There are still a couple bugs that need to be fixed, no node-gyp yet, and git
/github
dependencies don't work yetbun pm hash
returns a hash of the resolved all package@version in your lockfilebun install --global
lets you globally install npm packages with bun!TextEncoder
, TextDecoder
, process.exit
, crypto.randomUUID
, crypto.randomBytes
are now available in bun.js# Install a package globally with bun install
> bun install -g typescript
# "bun i" is shorthand for bun install
> bun i -g typescript
# It symlinks into your $BUN_INSTALL/bin folder
> which tsc
/Users/jarred/.bun/bin/tsc
# print the path of the global bin
> bun pm -g bin
/Users/jarred/.bun/bin
bun install
lockfile format change means your next bun install
will invalidate existing ones. The changes improve the reliability of saving and loading the lockfileBefore this release, bun dev
did not support displaying runtime errors. It only showed errors for build/ssr 🙈
Now you can move code from runtime to build-time by writing ordinary javascript.
Input:
// This line is removed at build time
import {weatherIn} from 'macro:./weather'
// This function call is replaced with the value of `weatherIn(94609)` at build-time
const {current_condition: [{temp_F: degrees}]} = weatherIn(94609);
export default `It is ${degrees} in Oakland, California`
weather.js:
export async function weatherIn(call) {
const location = call.arguments[0];
return fetch(`https://wttr.in/${location.toString()}?format=j1`)
}
Output:
const {
current_condition: [{ temp_F: degrees }]
} = {
current_condition: [
{
temp_F: "42"
}
]
};
// once bun gets a minifier, this line would be all that remains
export default `It is ${degrees} in Oakland, California`;
{
"current_condition": [
{
"FeelsLikeC": "4",
"FeelsLikeF": "39",
"cloudcover": "100",
"humidity": "79",
"localObsDateTime": "2022-02-25 03:18 AM",
"observation_time": "11:18 AM",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"temp_C": "4",
"temp_F": "40",
"uvIndex": "1",
"visibility": "16",
"visibilityMiles": "9",
"weatherCode": "122",
"weatherDesc": [
{
"value": "Overcast"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "ENE",
"winddirDegree": "70",
"windspeedKmph": "7",
"windspeedMiles": "4"
}
],
"nearest_area": [
{
"areaName": [
{
"value": "Emeryville"
}
],
"country": [
{
"value": "United States of America"
}
],
"latitude": "37.831",
"longitude": "-122.284",
"population": "0",
"region": [
{
"value": "California"
}
],
"weatherUrl": [
{
"value": ""
}
]
}
],
"request": [
{
"query": "Lat 37.83 and Lon -122.26",
"type": "LatLon"
}
],
"weather": [
{
"astronomy": [
{
"moon_illumination": "24",
"moon_phase": "Last Quarter",
"moonrise": "03:03 AM",
"moonset": "12:25 PM",
"sunrise": "06:46 AM",
"sunset": "05:59 PM"
}
],
"avgtempC": "10",
"avgtempF": "49",
"date": "2022-02-25",
"hourly": [
{
"DewPointC": "2",
"DewPointF": "36",
"FeelsLikeC": "5",
"FeelsLikeF": "42",
"HeatIndexC": "7",
"HeatIndexF": "44",
"WindChillC": "5",
"WindChillF": "42",
"WindGustKmph": "11",
"WindGustMiles": "7",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "82",
"chanceofsnow": "0",
"chanceofsunshine": "88",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "4",
"humidity": "75",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1025",
"pressureInches": "30",
"tempC": "7",
"tempF": "44",
"time": "0",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "WNW",
"winddirDegree": "300",
"windspeedKmph": "6",
"windspeedMiles": "4"
},
{
"DewPointC": "2",
"DewPointF": "35",
"FeelsLikeC": "6",
"FeelsLikeF": "42",
"HeatIndexC": "6",
"HeatIndexF": "42",
"WindChillC": "6",
"WindChillF": "42",
"WindGustKmph": "3",
"WindGustMiles": "2",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "81",
"chanceofsnow": "0",
"chanceofsunshine": "93",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "78",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "6",
"tempF": "42",
"time": "300",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "N",
"winddirDegree": "360",
"windspeedKmph": "2",
"windspeedMiles": "1"
},
{
"DewPointC": "1",
"DewPointF": "34",
"FeelsLikeC": "4",
"FeelsLikeF": "39",
"HeatIndexC": "5",
"HeatIndexF": "40",
"WindChillC": "4",
"WindChillF": "39",
"WindGustKmph": "9",
"WindGustMiles": "5",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "91",
"chanceofsnow": "0",
"chanceofsunshine": "94",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "78",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1025",
"pressureInches": "30",
"tempC": "5",
"tempF": "40",
"time": "600",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "ENE",
"winddirDegree": "72",
"windspeedKmph": "5",
"windspeedMiles": "3"
},
{
"DewPointC": "-1",
"DewPointF": "31",
"FeelsLikeC": "5",
"FeelsLikeF": "41",
"HeatIndexC": "6",
"HeatIndexF": "42",
"WindChillC": "5",
"WindChillF": "41",
"WindGustKmph": "7",
"WindGustMiles": "4",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "89",
"chanceofsnow": "0",
"chanceofsunshine": "87",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "64",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "6",
"tempF": "42",
"time": "900",
"uvIndex": "3",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "ENE",
"winddirDegree": "72",
"windspeedKmph": "5",
"windspeedMiles": "3"
},
{
"DewPointC": "0",
"DewPointF": "32",
"FeelsLikeC": "13",
"FeelsLikeF": "56",
"HeatIndexC": "13",
"HeatIndexF": "56",
"WindChillC": "13",
"WindChillF": "56",
"WindGustKmph": "8",
"WindGustMiles": "5",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "80",
"chanceofsnow": "0",
"chanceofsunshine": "85",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "40",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "13",
"tempF": "56",
"time": "1200",
"uvIndex": "4",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "ENE",
"winddirDegree": "70",
"windspeedKmph": "7",
"windspeedMiles": "4"
},
{
"DewPointC": "1",
"DewPointF": "33",
"FeelsLikeC": "17",
"FeelsLikeF": "62",
"HeatIndexC": "17",
"HeatIndexF": "62",
"WindChillC": "17",
"WindChillF": "62",
"WindGustKmph": "8",
"WindGustMiles": "5",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "89",
"chanceofsnow": "0",
"chanceofsunshine": "88",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "34",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1025",
"pressureInches": "30",
"tempC": "17",
"tempF": "62",
"time": "1500",
"uvIndex": "5",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "NNW",
"winddirDegree": "330",
"windspeedKmph": "7",
"windspeedMiles": "4"
},
{
"DewPointC": "2",
"DewPointF": "36",
"FeelsLikeC": "16",
"FeelsLikeF": "61",
"HeatIndexC": "16",
"HeatIndexF": "61",
"WindChillC": "16",
"WindChillF": "61",
"WindGustKmph": "9",
"WindGustMiles": "6",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "80",
"chanceofsnow": "0",
"chanceofsunshine": "93",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "39",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1024",
"pressureInches": "30",
"tempC": "16",
"tempF": "61",
"time": "1800",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "NW",
"winddirDegree": "307",
"windspeedKmph": "8",
"windspeedMiles": "5"
},
{
"DewPointC": "2",
"DewPointF": "35",
"FeelsLikeC": "8",
"FeelsLikeF": "47",
"HeatIndexC": "9",
"HeatIndexF": "48",
"WindChillC": "8",
"WindChillF": "47",
"WindGustKmph": "12",
"WindGustMiles": "7",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "87",
"chanceofsnow": "0",
"chanceofsunshine": "85",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "61",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1025",
"pressureInches": "30",
"tempC": "9",
"tempF": "48",
"time": "2100",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "NW",
"winddirDegree": "305",
"windspeedKmph": "6",
"windspeedMiles": "4"
}
],
"maxtempC": "17",
"maxtempF": "63",
"mintempC": "4",
"mintempF": "39",
"sunHour": "11.0",
"totalSnow_cm": "0.0",
"uvIndex": "4"
},
{
"astronomy": [
{
"moon_illumination": "17",
"moon_phase": "Waning Crescent",
"moonrise": "04:08 AM",
"moonset": "01:29 PM",
"sunrise": "06:44 AM",
"sunset": "06:00 PM"
}
],
"avgtempC": "10",
"avgtempF": "51",
"date": "2022-02-26",
"hourly": [
{
"DewPointC": "2",
"DewPointF": "35",
"FeelsLikeC": "7",
"FeelsLikeF": "45",
"HeatIndexC": "8",
"HeatIndexF": "46",
"WindChillC": "7",
"WindChillF": "45",
"WindGustKmph": "8",
"WindGustMiles": "5",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "83",
"chanceofsnow": "0",
"chanceofsunshine": "91",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "67",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "8",
"tempF": "46",
"time": "0",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "N",
"winddirDegree": "353",
"windspeedKmph": "4",
"windspeedMiles": "3"
},
{
"DewPointC": "0",
"DewPointF": "33",
"FeelsLikeC": "7",
"FeelsLikeF": "45",
"HeatIndexC": "7",
"HeatIndexF": "45",
"WindChillC": "7",
"WindChillF": "45",
"WindGustKmph": "7",
"WindGustMiles": "4",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "91",
"chanceofsnow": "0",
"chanceofsunshine": "94",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "12",
"humidity": "62",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1025",
"pressureInches": "30",
"tempC": "7",
"tempF": "45",
"time": "300",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "E",
"winddirDegree": "90",
"windspeedKmph": "4",
"windspeedMiles": "2"
},
{
"DewPointC": "-1",
"DewPointF": "31",
"FeelsLikeC": "6",
"FeelsLikeF": "42",
"HeatIndexC": "7",
"HeatIndexF": "44",
"WindChillC": "6",
"WindChillF": "42",
"WindGustKmph": "11",
"WindGustMiles": "7",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "81",
"chanceofsnow": "0",
"chanceofsunshine": "92",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "9",
"humidity": "61",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "7",
"tempF": "44",
"time": "600",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "ENE",
"winddirDegree": "66",
"windspeedKmph": "6",
"windspeedMiles": "4"
},
{
"DewPointC": "-1",
"DewPointF": "31",
"FeelsLikeC": "6",
"FeelsLikeF": "43",
"HeatIndexC": "7",
"HeatIndexF": "45",
"WindChillC": "6",
"WindChillF": "43",
"WindGustKmph": "9",
"WindGustMiles": "5",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "83",
"chanceofsnow": "0",
"chanceofsunshine": "92",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "4",
"humidity": "57",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1027",
"pressureInches": "30",
"tempC": "7",
"tempF": "45",
"time": "900",
"uvIndex": "3",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "ENE",
"winddirDegree": "71",
"windspeedKmph": "6",
"windspeedMiles": "4"
},
{
"DewPointC": "1",
"DewPointF": "33",
"FeelsLikeC": "14",
"FeelsLikeF": "57",
"HeatIndexC": "13",
"HeatIndexF": "56",
"WindChillC": "14",
"WindChillF": "57",
"WindGustKmph": "4",
"WindGustMiles": "2",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "83",
"chanceofsnow": "0",
"chanceofsunshine": "89",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "7",
"humidity": "43",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1027",
"pressureInches": "30",
"tempC": "13",
"tempF": "56",
"time": "1200",
"uvIndex": "4",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "NE",
"winddirDegree": "48",
"windspeedKmph": "4",
"windspeedMiles": "2"
},
{
"DewPointC": "2",
"DewPointF": "35",
"FeelsLikeC": "15",
"FeelsLikeF": "60",
"HeatIndexC": "15",
"HeatIndexF": "60",
"WindChillC": "15",
"WindChillF": "60",
"WindGustKmph": "5",
"WindGustMiles": "3",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "88",
"chanceofsnow": "0",
"chanceofsunshine": "86",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "12",
"humidity": "39",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "15",
"tempF": "60",
"time": "1500",
"uvIndex": "5",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "WNW",
"winddirDegree": "296",
"windspeedKmph": "5",
"windspeedMiles": "3"
},
{
"DewPointC": "1",
"DewPointF": "33",
"FeelsLikeC": "13",
"FeelsLikeF": "55",
"HeatIndexC": "13",
"HeatIndexF": "56",
"WindChillC": "13",
"WindChillF": "55",
"WindGustKmph": "12",
"WindGustMiles": "8",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "39",
"chanceofrain": "0",
"chanceofremdry": "91",
"chanceofsnow": "0",
"chanceofsunshine": "80",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "49",
"humidity": "42",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1027",
"pressureInches": "30",
"tempC": "13",
"tempF": "56",
"time": "1800",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "116",
"weatherDesc": [
{
"value": "Partly cloudy"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "W",
"winddirDegree": "278",
"windspeedKmph": "9",
"windspeedMiles": "5"
},
{
"DewPointC": "0",
"DewPointF": "32",
"FeelsLikeC": "11",
"FeelsLikeF": "51",
"HeatIndexC": "11",
"HeatIndexF": "52",
"WindChillC": "11",
"WindChillF": "51",
"WindGustKmph": "12",
"WindGustMiles": "8",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "90",
"chanceofrain": "0",
"chanceofremdry": "87",
"chanceofsnow": "0",
"chanceofsunshine": "16",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "77",
"humidity": "46",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1027",
"pressureInches": "30",
"tempC": "11",
"tempF": "52",
"time": "2100",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "119",
"weatherDesc": [
{
"value": "Cloudy"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "E",
"winddirDegree": "90",
"windspeedKmph": "7",
"windspeedMiles": "4"
}
],
"maxtempC": "15",
"maxtempF": "60",
"mintempC": "6",
"mintempF": "42",
"sunHour": "11.6",
"totalSnow_cm": "0.0",
"uvIndex": "4"
},
{
"astronomy": [
{
"moon_illumination": "9",
"moon_phase": "Waning Crescent",
"moonrise": "05:05 AM",
"moonset": "02:39 PM",
"sunrise": "06:43 AM",
"sunset": "06:01 PM"
}
],
"avgtempC": "13",
"avgtempF": "56",
"date": "2022-02-27",
"hourly": [
{
"DewPointC": "0",
"DewPointF": "32",
"FeelsLikeC": "11",
"FeelsLikeF": "51",
"HeatIndexC": "11",
"HeatIndexF": "51",
"WindChillC": "11",
"WindChillF": "51",
"WindGustKmph": "1",
"WindGustMiles": "0",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "92",
"chanceofrain": "0",
"chanceofremdry": "86",
"chanceofsnow": "0",
"chanceofsunshine": "11",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "74",
"humidity": "47",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1028",
"pressureInches": "30",
"tempC": "11",
"tempF": "51",
"time": "0",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "119",
"weatherDesc": [
{
"value": "Cloudy"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "WNW",
"winddirDegree": "297",
"windspeedKmph": "0",
"windspeedMiles": "0"
},
{
"DewPointC": "0",
"DewPointF": "32",
"FeelsLikeC": "10",
"FeelsLikeF": "50",
"HeatIndexC": "10",
"HeatIndexF": "50",
"WindChillC": "10",
"WindChillF": "50",
"WindGustKmph": "7",
"WindGustMiles": "4",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "38",
"chanceofrain": "0",
"chanceofremdry": "85",
"chanceofsnow": "0",
"chanceofsunshine": "86",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "29",
"humidity": "51",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1028",
"pressureInches": "30",
"tempC": "10",
"tempF": "50",
"time": "300",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "116",
"weatherDesc": [
{
"value": "Partly cloudy"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "NW",
"winddirDegree": "326",
"windspeedKmph": "4",
"windspeedMiles": "2"
},
{
"DewPointC": "0",
"DewPointF": "32",
"FeelsLikeC": "9",
"FeelsLikeF": "48",
"HeatIndexC": "9",
"HeatIndexF": "48",
"WindChillC": "9",
"WindChillF": "48",
"WindGustKmph": "7",
"WindGustMiles": "4",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "92",
"chanceofsnow": "0",
"chanceofsunshine": "91",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "16",
"humidity": "54",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1029",
"pressureInches": "30",
"tempC": "9",
"tempF": "48",
"time": "600",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "N",
"winddirDegree": "1",
"windspeedKmph": "4",
"windspeedMiles": "2"
},
{
"DewPointC": "0",
"DewPointF": "32",
"FeelsLikeC": "10",
"FeelsLikeF": "51",
"HeatIndexC": "10",
"HeatIndexF": "50",
"WindChillC": "10",
"WindChillF": "51",
"WindGustKmph": "6",
"WindGustMiles": "4",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "86",
"chanceofsnow": "0",
"chanceofsunshine": "88",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "10",
"humidity": "50",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1030",
"pressureInches": "30",
"tempC": "10",
"tempF": "50",
"time": "900",
"uvIndex": "4",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "ENE",
"winddirDegree": "72",
"windspeedKmph": "4",
"windspeedMiles": "2"
},
{
"DewPointC": "2",
"DewPointF": "35",
"FeelsLikeC": "17",
"FeelsLikeF": "62",
"HeatIndexC": "17",
"HeatIndexF": "62",
"WindChillC": "17",
"WindChillF": "62",
"WindGustKmph": "5",
"WindGustMiles": "3",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "94",
"chanceofsnow": "0",
"chanceofsunshine": "92",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "7",
"humidity": "36",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1029",
"pressureInches": "30",
"tempC": "17",
"tempF": "62",
"time": "1200",
"uvIndex": "5",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "NNE",
"winddirDegree": "30",
"windspeedKmph": "4",
"windspeedMiles": "2"
},
{
"DewPointC": "3",
"DewPointF": "37",
"FeelsLikeC": "19",
"FeelsLikeF": "66",
"HeatIndexC": "19",
"HeatIndexF": "66",
"WindChillC": "19",
"WindChillF": "66",
"WindGustKmph": "9",
"WindGustMiles": "6",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "91",
"chanceofsnow": "0",
"chanceofsunshine": "91",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "3",
"humidity": "34",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "19",
"tempF": "66",
"time": "1500",
"uvIndex": "5",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "WNW",
"winddirDegree": "299",
"windspeedKmph": "8",
"windspeedMiles": "5"
},
{
"DewPointC": "5",
"DewPointF": "42",
"FeelsLikeC": "18",
"FeelsLikeF": "64",
"HeatIndexC": "18",
"HeatIndexF": "64",
"WindChillC": "18",
"WindChillF": "64",
"WindGustKmph": "12",
"WindGustMiles": "7",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "83",
"chanceofsnow": "0",
"chanceofsunshine": "90",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "0",
"humidity": "43",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1026",
"pressureInches": "30",
"tempC": "18",
"tempF": "64",
"time": "1800",
"uvIndex": "5",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Sunny"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "W",
"winddirDegree": "271",
"windspeedKmph": "10",
"windspeedMiles": "6"
},
{
"DewPointC": "5",
"DewPointF": "42",
"FeelsLikeC": "12",
"FeelsLikeF": "53",
"HeatIndexC": "12",
"HeatIndexF": "53",
"WindChillC": "12",
"WindChillF": "53",
"WindGustKmph": "11",
"WindGustMiles": "7",
"chanceoffog": "0",
"chanceoffrost": "0",
"chanceofhightemp": "0",
"chanceofovercast": "0",
"chanceofrain": "0",
"chanceofremdry": "83",
"chanceofsnow": "0",
"chanceofsunshine": "87",
"chanceofthunder": "0",
"chanceofwindy": "0",
"cloudcover": "16",
"humidity": "64",
"precipInches": "0.0",
"precipMM": "0.0",
"pressure": "1027",
"pressureInches": "30",
"tempC": "12",
"tempF": "53",
"time": "2100",
"uvIndex": "1",
"visibility": "10",
"visibilityMiles": "6",
"weatherCode": "113",
"weatherDesc": [
{
"value": "Clear"
}
],
"weatherIconUrl": [
{
"value": ""
}
],
"winddir16Point": "WNW",
"winddirDegree": "292",
"windspeedKmph": "6",
"windspeedMiles": "4"
}
],
"maxtempC": "20",
"maxtempF": "68",
"mintempC": "9",
"mintempF": "47",
"sunHour": "11.6",
"totalSnow_cm": "0.0",
"uvIndex": "3"
}
]
}
I ported some of esbuild's tests to bun's testing library and that led to uncovering & fixing some parser bugs
// what some of the tests look like
expectParseError("-x.y() ** 2", "Unexpected **");
expectParseError("+x.y() ** 2", "Unexpected **");
expectParseError("~x.y() ** 2", "Unexpected **");
expectParseError("!x.y() ** 2", "Unexpected **");
expectParseError("void x.y() ** 2", "Unexpected **");
expectParseError("delete x.y() ** 2", "Unexpected **");
expectParseError("typeof x.y() ** 2", "Unexpected **");
type
inside of import clauses is now supported. This is a TypeScript feature.
// previously, this would error
import {type FooType} from 'bar';
Import assertions are now parsed by bun. Currently, they're removed from the final output
import("./foo.json", { type: "json" });
// ⬇️
import("./foo.json");
import json from "./foo.json" assert { type: "json" };
// ⬇️
import json from "./foo.json";
Fixed a number of whitespace/formatting bugs with the printer
This release brings bun install
much closer to something you can use daily. It's not there yet, but it is close.
Somewhat controversially, bun install
uses a binary lockfile format. One thing several people asked for: a hash. So I added a hash.
> bun pm hash
1A0FF3DFFE05333A-b20fd0e1259d2de5-9E3B657EA8BD4436-9f3132e0eb95a60e
This is a SHA512/256 hash of the following string. The string is created by looping through all the resolved packages in the lockfile, sorting alphabetically, then sorting any packages with multiple duplicate versions.
> bun pm hash-string
-- BEGIN SHA512/256(`${alphabetize(name)}@${order(version)}`) --
@types/[email protected]
@types/[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
-- END HASH--
bun pm
is a new subcommand for "miscellaneous package manager things"
> bun pm
bun pm - package manager related commands
bun pm bin print the path to bin folder
bun pm -g bin print the global path to bin folder
bun pm hash generate & print the hash of the current lockfile
bun pm hash-string print the string used to hash the lockfile
bun pm hash-print print the hash stored in the current lockfile
When using bun install -y
, the hash is inserted into the saved yarn.lock:
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
# bun ./bun.lockb --hash: 3982DAEC20B10CF8-4da4f73e6f1d3ab1-77B62BB40B50C631-a7674fe7d3217550
You can now configure bun install
with bunfig.toml
. it will search for bunfig.toml
in $XDG_CONFIG_HOME/bunfig.toml
and $HOME/bunfig.toml
, in addition to ./bunfig.toml
bun run
bun bun
Open-in-editor is now implemented
ShadowRealm
supportTextEncoder
and TextDecoder
implementationprocess.exit(exitCode)
is now implementedcrypto.randomUUID
and crypto.randomBytes()
setTimeout
, setInterval
, clearTimeout
, clearInterval
are stubbed in to bun.js. They do not actually work yet, but at least code that calls it won't error anymoretry running bun wiptest
in a project that uses jest
it'll probably throw a not implemented error, but it might work
Published by Jarred-Sumner over 2 years ago
To upgrade:
bun upgrade
# If you run into problems, try this instead:
curl https://bun.sh/install | bash
Highlights:
bun install
, fetch()
, bun create
, bun upgrade
, and everything else depending on itbunfig.toml
lets you configure bun from a config file instead of only CLI argumentsimport foo from "./file.toml"
is now supported (thanks to bun's new TOML parser)react-refresh
npm package for React Fast Refresh to workpath
modulebun dev
includes a default favicon, and the favicon becomes a red ⚠️ on build errorI rewrote the TLS part of bun's https client to match Chromium's behavior.
This fixes many bugs:
bun dev
would crash after 30 seconds due to bugs with the https client. Since it happened in a random interval between 0 and 30 seconds, it appeared to be in many placesbun install
or data corruptiongetaddrinfo
. On Linux, this sometimes caused it to crash. Now bun does not cache it. This makes it a little slower, but this way of doing DNS resolution needs to be replaced with a non-blocking implementation regardless.bunfig.toml is bun's configuration file.
Everything in it is optional. You don't need a bunfig.toml
to use bun, this exists so you don't have to pass flags to the CLI each time.
Options specific to bun install
will be added in a future release.
# Set a default framework to use
# By default, bun will look for an npm package like `bun-framework-${framework}`, followed by `${framework}`
framework = "next"
logLevel = "debug"
# publicDir = "public"
# external = ["jquery"]
[macros]
# Remap any import like this:
# import {graphql} from 'react-relay';
# To:
# import {graphql} from 'macro:bun-macro-relay';
react-relay = { "graphql" = "bun-macro-relay" }
[bundle]
saveTo = "node_modules.bun"
# Don't need this if `framework` is set, but showing it here as an example anyway
entryPoints = ["./app/index.ts"]
[bundle.packages]
# If you're bundling packages that do not actually live in a `node_modules` folder or do not have the full package name in the file path, you can pass this
"@bigapp/design-system" = true
[dev]
# Change the default port from 3000 to 5000
port = 5000
[define]
# Replace any usage of "process.env.bagel" with the string `lox`.
# The values are parsed as JSON, except single-quoted strings are supported and `'undefined'` becomes `undefined` in JS.
# This will probably change in a future release to be just regular TOML instead. It is a holdover from the CLI argument parsing.
"process.env.bagel" = "'lox'"
[loaders]
# When loading a .bagel file, run the JS parser
".bagel" = "js"
bun now has a custom TOML 1.0 parser, which means you can import .toml files from bun.
Note that parsing dates/timestamps is not implemented yet.
import config from 'my-config-file.toml';
console.assert(config.hi === "hello");
hi = "hello"
A future version might support importing top-level properties instead of only default
. I think supporting both ways would be a better DX.
This release improves using bun for static sites. bun dev
now automatically looks for .html
files in the same folder as code. Previously, .html
files needed to be in either public
or static
You no longer need to install the react-refresh
npm package to use React Fast Refresh with bun. bun embeds it into bun's binary and automatically enables it if resolving "react"
(or otherwise, the JSX import source) is successful
bun sets a default favicon so you can more easily see which tab is your dev server:
bun.js gets native support for Node's path
module. This is a performance improvement, but it's also good for consistency & reliability. It means that resolving file paths in JavaScript is consistent with native code.
// Any of these work
import * as path from "node:path";
import * as path from "path";
const path = require("path");
As part of that work, a bug was fixed when generating relative file paths where, in some cases, it would cut off the first letter if the destination
file path did not have any folders in common with the from
file path
Misc:
require()
bundled code - 73449bf43373ee3a23f080e3b9ce144bc93db27cThanks to:
Published by Jarred-Sumner over 2 years ago
To upgrade:
bun upgrade
This release is mostly focused on bun.js, bun's JavaScript runtime environment. Fixes to bun install
will be part of the next release, but the infrastructure work from this release will help with bun install
in the next release.
TLDR:
Bun.Transpiler
lets you run Bun's JS/TSX transpiler programmatically from bun.jsprocess
objectBun.Transpiler
- API access to Bunconst bun = new Bun.Transpiler({
loader: "tsx", // set default loader
});
// logs transpiled code without resolving imports
console.log(bun.transformSync("export default <div />"));
// return list of imports & exports without resolving imports or printing code
const { imports, exports } = bun.scan(`
import { Component } from "react";
export const foo: boolean = true;
`);
console.log({ exports, imports });
Bun.Transpiler
exposes part of Bun's JavaScript & TypeScript transpiler from native code to JavaScript, and it's fast.
End-to-end, transpiling this JSX file inside JavaScript via Bun.Transpiler
runs:
Bun.Transpiler
supports JavaScript plugins with AST access via macros. Macros are not entirely done yet, but simple ones work. There will be docs on this.
Bun.Transpiler
is builtin to bun.js, there's nothing extra to import or install. Eventually there will be a WASM build for some of this, but not sure when
However, a transpiler API is not very useful without a way to read/write files to disk.
fs
module implementation// This works in bun.js now
import {readFileSync} from 'fs';
// require("fs") also works in both .mjs files and .js/.cjs files
require("fs").writeFileSync("foo.txt", "bar!")
// you can also use the node: namespace if you want
import {readlinkSync} from 'node:fs';
You can now use most of the sync functions from Node.js' fs
module inside bun.js. These are implemented from scratch in Zig & exposed to JS. Buffer
& streams are not implemented yet, but you can pass a Uint8Array
or ArrayBuffer
where Node accepts a Buffer
. The async versions of the functions will come in a future release (this already was a lot of stuff for one release), but generally sync outperforms async for local file access.
fs.realpathSync
is about 7x faster in bun.js (50,000 iterations)
fs.existsSync
runs about 30% faster in bun.js (100,000 iterations)
The following functions are implemented:
fs.accessSync
fs.appendFileSync
fs.chmodSync
fs.chownSync
fs.closeSync
fs.copyFileSync
fs.existsSync
fs.fchmodSync
fs.fchownSync
fs.fstatSync
fs.fsyncSync
fs.ftruncateSync
fs.futimesSync
fs.lchmodSync
fs.lchownSync
fs.linkSync
fs.lstatSync
fs.lutimesSync
fs.mkdirSync
fs.openSync
fs.readdirSync
fs.readFileSync
fs.readlinkSync
fs.readSync
fs.realpathSync
fs.renameSync
fs.statSync
fs.symlinkSync
fs.truncateSync
fs.unlinkSync
fs.utimesSync
fs.writeFileSync
fs.writeSync
Bun also includes an implementation of Node's SystemError
with pretty printing. Note that since source maps are not implemented yet, sometimes the line:column will be off by a little.
This is what the same error looks like in Node
process
objectbun.js has more support for the process
object from Node.js.
// These work now in bun.js
process.chdir("insert-dir-name-here");
process.cwd();
// arguments used to launch, excluding "run" if via "bun run" for compatibility with npm packages
process.argv;
// current process id
process.pid;
// parent process pid
process.ppid;
// returns bun's version
process.version
process.versions
{
// fake version for compatibility with npm packages potentially looking this up
"node": "17.0.0",
"modules": "67",
// bun version
"bun": "0.0.68",
// git shas/tag of bun dependencies
"webkit": "96e77eccfde8dc9c207520d8ced856d8bdb8d386",
"mimalloc": "f412df7a2b64421e1f1d61fde6055a6ea288e8f5",
"libarchive": "dc321febde83dd0f31158e1be61a7aedda65e7a2",
"picohttpparser": "066d2b1e9ab820703db0837a7255d92d30f0c9f5",
"boringssl": "b3ed071ecc4efb77afd0a025ea1078da19578bfd",
"zlib": "959b4ea305821e753385e873ec4edfaa9a5d49b7",
"zig": "0.10.0-dev.315+4d05f2ae5"
}
// process.nextTick() is now implemented (currently only supports up to 4 arguments)
process.nextTick(() => console.log("second"));
console.log("first");
import.meta
in bun.js returns an object with file
and dir
const {
// import.meta.dir returns absolute path to the directory the script is in. sort of like __dirname
dir,
// import.meta.file returns absolute path to the script
file,
} = import.meta;
queueMicrotask
is implemented./
to run a script with bun.js, e.g. instead of bun ./foo.js
, bun foo.js
works now.mjs
or .mts
files now and treats them as ESM. Before it was reading them but ignoring the extensionBun.gc(force)
lets you manually run the garbage collectorBun.shrink()
runs a JavaScriptCore VM function that attempts to shrink the amount of memory used by JavaScriptCoreBun.generateHeapSnapshot()
returns a heap snapshot in a format I'm not entirely sure how to visualize yetIf you look hard enough, you'll also find a new subcommand for a very incomplete but fast Jest-like test runner. Hopefully will talk more about that next release or the one after.
Published by Jarred-Sumner almost 3 years ago
To upgrade:
bun upgrade
These changes are since bun v0.0.56 (the previous release notes 13 days ago)
TLDR:
bun install
when packages aren't downloadedbun install
(but still more work to do!)~25% faster when downloading lots of new packages. devDependencies were incorrectly being prioritized.
Plus:
bun add @scoped/package
works now>=
ranges for package versions work correctly nowbun install
would error with error: SystemResources
. This is an issue with the memlock
limit that impacts io_uring. Now bun lowers memlock usage when this error returns until it finds a value that worksbun install
will delete the lockfile/tmp
was mounted on a different filesystem, extracting packages failed with error: RenameAcrossMountPoints
. Now bun tests if it can rename files from the temporary directory to the cache directory and chooses a different temporary directory if it cannotbun install --production
works better now. Just before installing, it clones the original lockfile in-memory and then removes any devDependencies listed in the root package.json.Improved filesystem watcher reliability on Linux
bun now handles atomic file updates better in the filesystem watcher. To filesystem watchers, atomic file updates appear as a delete followed by a new file being moved to an existing directory. bun previously only noticed the delete. Most editors do not save atomically, but if vim swapfiles are enabled or if using replit, this may help.
Improved support for reverse-proxying bun
Due to same-origin policy, bun's HMR needs to use absolute URLs that match what the browser expects. Previously, to proxy bun you had to pass --origin
to bun dev
and it would maybe still not work for https
. Now bun reads headers proxies send & what browsers send to determine which protocol, host, and/or origin is expected.
Misc:
@tailwind
is not supported instead of on every single request to that .css filefetch()
in SSR. This is fixedURL
polyfill broke navigation in some cases. Now there is a URL polyfill. Eventually, bun.js will have this as a builtin implemented in native code.Bun.argv
which returns string[]
containing the CLI arguments used to open the currently running process. It is basically process.argv
If you're interested in contributing to bun, you can now use a VSCode DevContainer to quickly setup the dev environment. Note that it currently requires at least 11 GB of ram in the dockerized OS to compile debug builds of bun.
bun now has automatic docker releases for Linux AMD64 compiled on every push to main
. The tag name is jarredsumner/bun:${gitSHA}
bun is now using the latest version of Zig and LLVM 13, instead of a hacky patched version of Zig. This was a large change affecting basically every file in bun.
5% faster JavaScript parser
If bun crashes, it reports a little more metadata now and (on macOS) save a crash report to disk.
@alexkuz wrote a Sublime Text plugin that opens bun.lockb (the lockfile for bun install
) as a yarn.lock file and adds syntax highlighting. Thank you @alexkuz!
GitHub: https://github.com/alexkuz/sublime-yarn-lock
Bun
with bun
Published by Jarred-Sumner almost 3 years ago
To upgrade:
bun upgrade
Today is an exciting day. Introducing bun install
– an incredibly fast npm client.
Before I get into perf numbers, consider this especially early.
link:
not implemented yetgithub:
not implemented yetgit:
not implemented yet$npm_config_registry
)esbuild
and turbo
get special treatment I'm calling "native bin linking". I plan to write a more generalizable proposal for this that other npm clients can adopt to make shipping binary executables on npm faster & simplernode-gyp
isn't supported yet either, but that will be fixed.100x faster npm install for incremental installs. In this case, only react is missing (the cleanup resets node_modules), each package manager has an up-to-date lockfile, and packages have previously downloaded before (disk cache)
20x faster than npm install when node_modules is empty. In this case, each package manager has an up-to-date lockfile and packages have also been downloaded before.
When there are no changes, bun install
is 100x faster than npm install
Note: currently macOS installs are single-threaded & parallelism may help when there are > 100 packages, so these numbers may improve later
80x faster npm install for incremental installs.
4x faster npm install when there is no node_modules folder
When there are no changes, 80x faster than npm install
Thanks to @evanwashere, bun add
has autocomplete for the top 10,000 npm packages. It also searches your shell's history to complete from.
Fish completions:
I will do a longer write up on why it's faster. Zig is part of that, but a lot of work went into data layout, the lockfile format, the https client, the manifest cache format, and a lot of other stuff.
Published by Jarred-Sumner almost 3 years ago
This release is mostly just bug fixes.
Bun now preserves the behavior of live bindings when referencing bundled symbols. This fixes a number of subtle bugs in different packages.
This also updates the preferred extension order depending on how code is imported. Previously, .mjs
files were ignored and that was silly. Now, when you use import
and the path has no file extension, Bun will attempt to load .mjs
or .mts
before trying .js
or .cjs
// CommonJS or `require`, `require.resolve`
// configurable via --extension-order CLI flag
pub const ExtensionOrder = [_]string{
".tsx",
".ts",
".jsx",
".cts",
".cjs",
".js",
".mjs",
".mts",
".json",
};
// ES Modules or `import`
pub const ModuleExtensionOrder = [_]string{
".tsx",
".jsx",
".mts",
".ts",
".mjs",
".js",
".cts",
".cjs",
".json",
};
Before, this code caused an assertion failure in Bun's JavaScript parser due to the function being defined in the template tag:
import styled from 'styled-components'
export const HoverableBox = styled.div.attrs<{
disabled?: boolean
}>(({ disabled }) => ({
cursor: disabled ? undefined : 'pointer',
}))<{ disabled?: boolean }>`
${({ disabled }) => (disabled ? 'pointer-events: none;' : '')}
`
The problem was related to when scopes for template literal tags were visited. This has been fixed.
try
& require()
Previously, this code would produce a build error:
try {
require("this-package-should-not-exist");
} catch (exception) {}
try {
await import("this-package-should-not-exist");
} catch (exception) {}
import("this-package-should-not-exist").then(
() => {},
() => {}
);
In each of these cases, errors are caught at runtime, so it should not produce a build error.
Top-level await will no longer error when targeting browsers, however you should know that since Bun does not transpile for backwards compatibility, you will need to be sure it is safe to use in the target environment.
Using module.exports
with top-level await is undefined behavior
Published by Jarred-Sumner almost 3 years ago
To upgrade:
bun upgrade
Like last release, this one is mostly bug fixes.
The zsh completions now include flags, descriptions of subcommands, and "scripts"
appear above package bins.
Also:
post.*
and pre.*
scripts from completions.
from completionsThe plugin for the typeahead here is zsh-autocomplete
For absolutely no good reason, Bun was not able to run on any macOS version before macOS 11. Now bun should work for macOS Mojave and macOS Catalina.
However, I don't have a test machine so please let me know if it still doesn't work.
/*.extension
in "exports" package.json and improve test coverageCommand not found: compdef
previously appeared if zsh completions weren't installed. That's fixed nowPublished by Jarred-Sumner almost 3 years ago
To upgrade:
bun upgrade
This release is mostly bug fixes.
bun run
instead of bun
"script"
, now it's the actual scriptNODE_OPTIONS=".*"
(or any variation of NODE_OPTIONS
), it's stripped out from the description. This is slightly nicer for larger repos that set --max-heap-size
bun
bun run
:
bun run yarn
or a script that runs yarn --prod
node
when using Volta (symlinks)&
) and improve test coveragerequire("faker")
Full Changelog: https://github.com/Jarred-Sumner/bun/compare/bun-v0.0.44...bun-v0.0.45
Published by Jarred-Sumner almost 3 years ago
To upgrade:
bun upgrade
Bun works with Next.js 12 now. Before, it only supported 11.1.2. This includes support for React 18 (react@alpha
). React Server Components is not supported yet, but it will be. I promise :)
bun run
) ZSH completions should auto-install correctly now. This will automatically apply when you run bun upgrade
bun dev
) Fixed a mistranspilation when using require
from app code to a bundled module (e.g. require("relay-devtools")
)bun bun
) Fixed an issue with symlinked workspace packages when using alwaysBundle
that prevented them from being correctly imported if enough levels of indirection were addedbun-macro-relay
) Worked around a bug that could cause it to not correctly import graphql
depending on the environmentbun create
) bumped the hardcoded Next.js version set to 12.0.2
Bun.sleep
-> Bun.sleepSync
Full Changelog: https://github.com/Jarred-Sumner/bun/compare/bun-v0.0.42...bun-v0.0.44
Published by Jarred-Sumner almost 3 years ago
To upgrade:
bun upgrade
bun run
now has tab completions for zsh and fish that show scripts
from package.json and bins from node_modules/.bin
.
Install:
bun upgrade
bun completions
Going forward, bun upgrade
also updates completions, so you shouldn't have to do that again.
Bun now has a builtin "supports-color"
polyfill, the package used by chalk
to detect ANSI color support.
The TypeScript in this screenshot runs 2x faster in Bun.js than in Node.js:
Other stuff:
node-fetch
& isomorphic-fetch
polyfills work now. When an npm package imports node-fetch
, Bun.js will automatically replace it with Bun's native implementation.Bun.sleep(ms)
lets you synchronously sleep. No promisesexport default
(thank you @evanwashere for flagging)\0
(and added an integration test)--version
now prints a newline at the end-v
is now shorthand for --version
-u
is now shorthand for --origin
. Might rename that to --url
in a future release.Published by Jarred-Sumner almost 3 years ago
To upgrade:
bun upgrade
bun run
:
bun run ./file.js
now supports running JavaScript, TS, TSX, and JSX files using Bun.js. If you pass bun run
a filepath to a .js
, .jsx
, .tsx
, or .ts
file, it will run it with Bun.js instead of saying "error: Missing script"
. This is very experimental!
Bun.js is Bun's JavaScript runtime environment.
performance.now()
is implemented.env
files which is unnecessary. Now process environment variables are passed through without extra parsingfetch()
wasn't working due to a silly mistake. That's fixedPublished by Jarred-Sumner almost 3 years ago
It's been a busy couple weeks.
Important: the bun-cli
npm package is deprecated. It will not receive updates.
Please upgrade Bun by running:
# Remove the npm version of bun
npm uninstall -g bun-cli
# Install the new version
curl https://bun.sh/install | bash
For future upgrades:
bun upgrade
Now the fun part.
~35x faster package.json "scripts"
runner, powered by Bun. Instead of npm run
, try bun run
.
Like npm run
, bun run
reads "scripts"
in your package.json and runs the script in a shell with the same PATH
changes as npm clients. Unlike npm clients, it's fast.
You can use bun run
in projects that aren't using Bun for transpiling or bundling. It only needs a package.json
file.
When scripts launch a Node.js process, bun run
still beats npm run
by 2x:
Why? Because npm clients launch an extra instance of Node.js. With npm run
, you wait for Node.js to boot twice. With bun run
, you wait once.
For maximum performance, if nested "scripts"
run other npm tasks, bun run
automatically runs the task with Bun instead. This means adding many tasks won't slow you down as much.
bun run
supports lifecycle hooks you expect like pre
and post
and works when the package.json
file is in a parent directory.
Two extra things:
bun run
also adds any node_modules/.bin
executables to the PATH, so e.g. if you're using Relay, you can do bun run relay-compiler
and it will run the version of relay-compiler
for the specific project.bun run
has builtin support for .env
. It reads: .env.local
, then .env.development
|| .env.production
(respecting your NODE_ENV
), and .env
in the current directory or enclosing package.json's directoryOh and you can drop the run
:
Lots of work went into improving the reliability of Bun over the last couple weeks.
Here's what I did:
bun create
to bootstrap the projects..env
loader. .env
values are now parsed as strings instead of something JSON-likeThere's still a lot of work ahead, but Bun is getting better.
Sometimes, speed and reliability is a tradeoff. This time, it's not. Bun seems to be about 3% faster overall compared to the last version (v0.0.36). Why? Mostly due to many small changes to the lexer. Inlining iterators is good.
Published by Jarred-Sumner about 3 years ago
bun create
is a new subcommand that lets you quickly create a project from a template.
To create a new React project (based on Create React App)
bun create react ./app
yarn create react app
on my Macbook Pro (both installing dependencies via yarn)To create a new Next.js project (based on Create Next App)
bun create next ./app
To create from a GitHub repository:
bun create jarred-sumner/calculator calc
To see a list of templates:
bun create
Right now, there are only two "official" templates (and they're mostly empty) – but you can add more. bun create
fetches from packages published in the examples
folder of Bun's GitHub repo. PRs are very welcome!
Many developers have boilerplate code copied for new projects.
That's why bun create
also searches $HOME/.bun-create
for local templates.
Add a folder with a package.json in $HOME/.bun-create/my-local-template
and then you can run:
bun create my-local-template ./foo
This copies the contents of my-local-template
, initializes a git repository, runs any preinstall steps, installs dependencies (if any exist), runs any postinstall steps and rewrites the name
field in package.json
to foo
.
It uses the fastest system calls available for copying files. bun create
copies files faster than cp
. Here is a stupid microbenchmark
The slowest part is running your NPM client to install node_modules
. The second slowest part is downloading the tarball from GitHub (or NPM if it's from Bun's examples folder). Third is git.
fetch
now supports gzipfetch
was completely rewritten to be more reliable. TLS is now powered by s2n-tls
fetch
now supports Transfer-Encoding: chunked
. It didn't before, which was embarrassing.self
to globalThis
in Bun.js. This improves compatibility with some npm packagesmisctools/
, tgz
and fetch
. tgz
is like gunzip
except harder to use and fetch
is sort of like curl
? These are really just to help me test some things in isolation from the rest of BunPublished by Jarred-Sumner about 3 years ago
Slightly better tsconfig.json
handling:
jsxImportSource
support – if you set jsxImportSource
to "@emotion/react"
, JSX will auto-import from "@emotion/react/jsx-dev-runtime"
instead of "react/jsx-dev-runtime"
jsxFragmentFactory
support – this lets you override what <>
transforms into. By default it's "Fragment"
, since <>
becomes React.Fragment
normally.jsxFactory
support – if you set jsxFactory
to "h"
, when using the classic JSX runtime, it will run h
instead of createElement
This makes it easier to use @emotion/react
, preact
, and other JSX runtimes with Bun.
children.length > 1
)bun bun
) that happened when resolving workspace packages above the project root that expect dependencies from the project. This applied to pnpmOther:
Published by Jarred-Sumner about 3 years ago
bun bun
on a networked filesystem. This is particularly relevant to WSL. The normal posix C function for moving files doesn't work if the source file is on a different mounted filesystem than the target. This fix detects the error and attempts to use sendfile() so that it copies the file faster than it would by reading the memory manually.Published by Jarred-Sumner about 3 years ago
5% end-to-end performance improvement by reducing allocations for Identifiers in the JavaScript/TypeScript parser.
Also, bun-cli
wasn't installing on Linux correctly. Now it is!
Published by Jarred-Sumner about 3 years ago
Bun is now available for Linux x64. That includes a filesystem watcher leveraging inotify
, Linux's API for watching files as well as macros and SSR/SSG.
When bundling 20 copies of Three.js on Linux x64, Bun performed:
babel-loader
)I have done very little work on optimizing Bun for Linux. I'm not satisfied with these numbers, but it's a start.
Bun uses the GOMAXPROCS
to control concurrency despite not being written in Go because I didn't want to think about what to name the variable
The runs with GOMAXPROCS=1
are single-threaded runs.
Bun single-threaded performed 2.2x faster than esbuild single-threaded. The relative gap between single-threaded and concurrent performance implies there's room for improvement in Bun's concurrency code. This machine has 6 cores (nproc
returns 12
) and 64 GB of memory.
Published by Jarred-Sumner about 3 years ago
You can now use Facebook's Relay GraphQL framework with Bun via bun-macro-relay
.
bun-macro-relay
implements the Bun equivalent of babel-plugin-relay
.
Input:
const query = graphql`
query Bacon {
}
`;
Output:
import Bacon from '__generated__/Bacon.graphql';
const query = Bacon;
This macro is implemented entirely in TypeScript - not in native code. For install instructions, head over to bun-macro-relay
.
// Instead of having to change every import like this:
import { css } from "@emotion/react";
// To this:
import { css } from "macro:bun-macro-emotion-react";
// Now you don't have to change your code
import { css } from "@emotion/react";
note: @emotion/react is not implemented yet!
To use this, add this to your package.json (and remove the comments):
"bun": {
"macros": {
// "react-relay" is the import path to rewrite
"react-relay": {
// rewrite the "graphql" import to instead point to "bun-macro-relay/bun-macro-relay.tsx"
"graphql": "bun-macro-relay/bun-macro-relay.tsx"
}
}
},
Remapping this way only applies to macros. To rewrite imports otherwise, Bun will read paths
from tsconfig.json
or jsconfig.json
.
source
feature flag is off and a runtime error occurs in a component being rendered<import>
lets you inject an import statement into a module. It accepts default
, which is the default import alias and you can pass a namespace
object where the object maps the import names to their names in the current file<id>
lets you reference an import (and eventually, other symbols too)<inject>
will insert each child element to the module scope. This currently only works with <import>
, but it will work with other statements eventually.Here's an example:
const importStmt = (
<import default="MyImportName" path="foo/bar" />
);
return (
<>
<inject>{importStmt}</inject>
<id to={importStmt.namespace.MyImportName} />
</>
);
Published by Jarred-Sumner about 3 years ago
Bun Macros are a simple way to move code snippets from runtime to build-time.
Use them for:
getStaticProps
but from any file instead of just pages)This API (and implementation) is very new and I want to hear feedback on what to change.
How to use macros:
macro:
is imported as a macro. Macros can be npm packages, local files, or anything else resolvable. These imports are always removed from the final output.Here's a quick example of using a macro:
// This import won't exist at runtime!
import { matchInFile } from "macro:./matchInFile";
export const IPAddresses = () => (
<div>
<h2>recent ip addresses</h2>
<div className="Lines">
{/* matchInFile gets inlined into an array at compile time */}
{matchInFile("access.log", /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}/).map(
(ipAddress, index) => (
<div className="Line" key={index}>
{ipAddress}
</div>
)
)}
</div>
</div>
);
The transpiled output looks something like this:
export const IPAddresses = () => jsx("div", {
children: [
jsx("h2", {
children: ["recent ip addresses"]
}, undefined, true, {}, this),
jsx("div", {
className: "Lines",
children: [[
"98.210.28.6",
"192.99.4.168",
// ... rest of the array
].map((ipAddress, index) => jsx("div", {
className: "Line",
children: [ipAddress]
}, index, true, {}, this))]
}, undefined, true, {}, this)
// ...rest of code
]
});
There are a few examples of how to write macros in https://github.com/Jarred-Sumner/bun/tree/main/examples/macros, but I will write more docs once I'm a little more confident that the API isn't going to change. Once I write macros for Relay and Emotion, I'll have a better sense for if this API is good enough or if there are changes to make.
I might change the import prefix from macro:
to bun:
because it's shorter and slightly faster to check (one u32 instead of a u32 and then a u16)
Bun Macros are inspired by babel-plugin-macros, which was Sunil Pai's idea. They're also sort of similar to Zig's comptime
feature.
Bun.readFileAsBytes
and Bun.readFile
now accept an array of paths to join. Both of these are valid:Option A:
Bun.readFile(Bun.cwd + "file.txt");
Option B:
Bun.readFile([Bun.cwd, "./text/", "file.txt"]);
Unlike Node.js' path.join
, it's an explicit array instead of arguments because someday, we might want to add extra options to this function.
Response.text()
(from fetch
)/
fetch
fetch
now includes a User-Agent
header by default. This is overridable, but it defaults to Bun.js v0.0.version-number
Published by Jarred-Sumner about 3 years ago
\f
form feed character (used in Emotion & Styled Components)"browser"
field in package.json. In some cases, it was resolving from the parent directory of the result instead of the browser scope.style
tag