-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: install esgleam on any platform (without curl)
- Loading branch information
1 parent
2bf9218
commit b7badb3
Showing
9 changed files
with
258 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,7 @@ | ||
import simplifile | ||
import esgleam/internal.{exec_shell} | ||
|
||
const cmd = "curl -fsSL https://esbuild.github.io/dl/latest | sh" | ||
import esgleam/internal/fetch_esbuild | ||
|
||
/// Installs `esbuild` (required to be run before building) | ||
/// Run with `gleam run -m esgleam/install` | ||
pub fn main() { | ||
let assert Ok(_) = simplifile.create_directory_all("./priv/bin") | ||
exec_shell(cmd, "./priv/bin") | ||
fetch_esbuild.fetch() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import gleam/io | ||
import gleam/string | ||
import esgleam/internal/platform | ||
|
||
const base_url = "https://registry.npmjs.org/{#version}/latest" | ||
|
||
@external(erlang, "ffi_esgleam", "do_fetch") | ||
@external(javascript, "../../ffi_esgleam.mjs", "do_fetch") | ||
fn do_fetch(url: String) -> Nil | ||
|
||
pub fn fetch() { | ||
let version = platform.get_package_name() | ||
let url = string.replace(base_url, "{#version}", version) | ||
io.println("Fetching esbuild from: " <> url) | ||
do_fetch(url) | ||
Nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
pub type OsName { | ||
Win32 | ||
Linux | ||
Darwin | ||
Solaris | ||
Sunos | ||
Freebsd | ||
Openbsd | ||
} | ||
|
||
pub type Arch { | ||
X64 | ||
Ia32 | ||
Arm64 | ||
Arm | ||
Ppc64 | ||
} | ||
|
||
@external(erlang, "ffi_esgleam", "get_os") | ||
@external(javascript, "../../ffi_esgleam.mjs", "get_os") | ||
pub fn get_os() -> Result(OsName, Nil) | ||
|
||
@external(erlang, "ffi_esgleam", "get_arch") | ||
@external(javascript, "../../ffi_esgleam.mjs", "get_arch") | ||
pub fn get_arch() -> Result(Arch, Nil) | ||
|
||
pub fn get_package_name() { | ||
let assert Ok(os) = get_os() | ||
let assert Ok(arch) = get_arch() | ||
|
||
let os_str = case os { | ||
Win32 -> "win32" | ||
Linux -> "linux" | ||
Darwin -> "darwin" | ||
Solaris | Sunos -> "sunos" | ||
Freebsd -> "freebsd" | ||
Openbsd -> "openbsd" | ||
} | ||
|
||
let arch_str = case arch { | ||
X64 -> "x64" | ||
Ia32 -> "ia32" | ||
Arm64 -> "arm64" | ||
Arm -> "arm" | ||
Ppc64 -> "ppc64" | ||
} | ||
|
||
"@esbuild/" <> os_str <> "-" <> arch_str | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,67 @@ | ||
-module(ffi_esgleam). | ||
|
||
-export([get_arch/0, get_os/0, do_fetch/1]). | ||
|
||
-spec get_arch() -> {ok, x64 | ia32 | arm64 | arm | ppc64} | {err, string()}. | ||
get_arch() -> | ||
% #TODO: find out how to detect if it's not ia32/x64 for windows :P | ||
case erlang:system_info(os_type) of | ||
{unix, _} -> | ||
[Arch_Raw, _] = string:split(erlang:system_info(system_architecture), "-"), | ||
% based on the values that `uname -a` returns | ||
case Arch_Raw of | ||
"x86_64" -> | ||
{ok, x64}; | ||
"amd64" -> | ||
{ok, x64}; | ||
"i386" -> | ||
{ok, ia32}; | ||
"i586" -> | ||
{ok, ia32}; | ||
"i686" -> | ||
{ok, ia32}; | ||
"aarch64" -> | ||
{ok, arm64}; | ||
"armv7l" -> | ||
{ok, arm}; | ||
"ppc64" -> | ||
{ok, ppc64}; | ||
_ -> {err, Arch_Raw} | ||
end; | ||
{win32, _} -> | ||
case erlang:system_info(wordsize) of | ||
4 -> {ok, ia32}; | ||
8 -> {ok, x64} | ||
end | ||
end. | ||
|
||
% list compiled from erlang otp source | ||
-spec get_os() -> {ok, win32 | darwin | linux | solaris | freebsd | openbsd | dragonfly | irix64 | irix | posix} | {err, atom()}. | ||
get_os() -> | ||
case erlang:system_info(os_type) of | ||
{win32, _} -> win32; | ||
{unix, linux} -> {ok, linux}; | ||
{unix, darwin} -> {ok, darwin}; | ||
{unix, sunos} -> {ok, solaris}; | ||
{unix, sunos4} -> {ok, sunos}; % technically not solaris | ||
{unix, freebsd} -> {ok, freebsd}; | ||
{unix, openbsd} -> {ok, openbsd}; | ||
{unix, Os} -> {err, Os} | ||
end. | ||
|
||
-spec do_fetch(string()) -> {}. | ||
do_fetch(Url) -> | ||
inets:start(), | ||
ssl:start(), | ||
{ok, {{_, 200, _}, _, Body}} = httpc:request(get, {Url, []}, [], []), | ||
{ok, Res} = thoas:decode(Body), | ||
#{<<"dist">> := #{<<"tarball">> := TarUrl}} = Res, | ||
fetch_tarball(TarUrl) | ||
. | ||
|
||
-spec fetch_tarball(string()) -> {}. | ||
fetch_tarball(Url) -> | ||
io:fwrite("Fetching tarball from: ~s\n", [Url]), | ||
{ok, {{_, 200, _}, _, Binary}} = httpc:request(get, {Url, []}, [], [{body_format, binary}]), | ||
ok = erl_tar:extract({binary, Binary}, [compressed, verbose, {cwd, "./priv"}, {files, ["package/bin/esbuild"]}]) | ||
. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,98 @@ | ||
import { exec, spawn } from "node:child_process"; | ||
import { exec, spawn } from 'node:child_process'; | ||
import { chmod, mkdir, writeFile } from 'node:fs/promises'; | ||
import * as process from 'node:process'; | ||
import { | ||
Win32, | ||
Linux, | ||
Darwin, | ||
Solaris, | ||
Freebsd, | ||
Openbsd, | ||
Arm, | ||
Arm64, | ||
Ia32, | ||
Ppc64, | ||
X64, | ||
// @ts-expect-error | ||
} from './esgleam/internal/platform.mjs'; | ||
// @ts-expect-error | ||
import { Ok, Error } from './gleam.mjs'; | ||
|
||
import { entries } from './streaming_tar.mjs'; | ||
|
||
export function exec_shell(command, cwd) { | ||
if (typeof command === "string") | ||
exec(command, { cwd, encoding: "utf-8" }, (_, stdout, stderr) => { | ||
if (typeof command === 'string') | ||
exec(command, { cwd, encoding: 'utf-8' }, (_, stdout, stderr) => { | ||
console.log(stdout); | ||
console.error(stderr); | ||
}); | ||
else if (typeof command === "object") { | ||
else if (typeof command === 'object') { | ||
command = command.toArray(); | ||
spawn(command[0], command.slice(1), { cwd, stdio: "inherit" }); | ||
spawn(command[0], command.slice(1), { cwd, stdio: 'inherit' }); | ||
} | ||
} | ||
|
||
/** @type {Partial<Record<NodeJS.Platform, () => import('./esgleam/internal/platform.gleam').OsName$>>} */ | ||
const platform_map = { | ||
darwin: () => new Darwin(), | ||
freebsd: () => new Freebsd(), | ||
linux: () => new Linux(), | ||
openbsd: () => new Openbsd(), | ||
sunos: () => new Solaris(), | ||
win32: () => new Win32(), | ||
}; | ||
|
||
export function get_os() { | ||
const platform = process.platform; | ||
const res = platform_map[platform](); | ||
if (res) return new Ok(res); | ||
return new Error(res); | ||
} | ||
|
||
/** @type {Partial<Record<NodeJS.Architecture, () => import('./esgleam/internal/platform.gleam').Arch$>>} */ | ||
const arch_map = { | ||
arm: () => new Arm(), | ||
arm64: () => new Arm64(), | ||
ia32: () => new Ia32(), | ||
ppc64: () => new Ppc64(), | ||
x64: () => new X64(), | ||
}; | ||
|
||
export function get_arch() { | ||
const arch = process.arch; | ||
const res = arch_map[arch](); | ||
if (res) return new Ok(res); | ||
return new Error(res); | ||
} | ||
|
||
/** | ||
* @param {string} url | ||
*/ | ||
export async function do_fetch(url) { | ||
const info_res = await fetch(url); | ||
if (!info_res.ok) | ||
return console.error( | ||
`Oh no. Something went wrong. Error fetching "${url}"\n${await info_res.text()}` | ||
); | ||
const content = await info_res.json(); | ||
const tarball_url = content.dist.tarball; | ||
console.log(`Fetching tarball from: ${tarball_url}`); | ||
const tarResp = await fetch(tarball_url); | ||
const tarStream = tarResp.body.pipeThrough(new DecompressionStream('gzip')); | ||
|
||
for await (const entry of entries(tarStream)) { | ||
if (entry.name == 'package/bin/esbuild') { | ||
console.log(entry.name); | ||
try { | ||
await mkdir('./priv/package/bin', { recursive: true }); | ||
} catch {} | ||
await writeFile( | ||
'./priv/package/bin/esbuild', | ||
Buffer.from(await entry.arrayBuffer()), | ||
{ encoding: 'binary' } | ||
); | ||
await chmod('./priv/package/bin/esbuild', 0o777); | ||
break; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
MIT License | ||
Copyright (c) 2023 Zebulon Piasecki | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
*/ | ||
|
||
var o=class{promise;resolve;constructor(){this.promise=new Promise(e=>this.resolve=e)}};var c=class{#e;#r;#s;#t;bodyUsed=!1;constructor(e,t,r,s){this.#e=e,this.#r=t,this.#s=s,this.#t=r.length>0?r:void 0}get name(){return this.#e.name}get fileSize(){return this.#e.fileSize}get#n(){let e=Math.ceil(this.#e.fileSize/512)*512;return new ReadableStream({start:()=>{if(this.bodyUsed)throw new Error("Body already used");this.bodyUsed=!0},pull:async t=>{if(e===0){t.close();return}let r;if(this.#t)r=this.#t,this.#t=void 0;else{let i=await this.#r.read();if(i.done){t.error(new Error("Unexpected end of stream"));return}r=i.value}let s=Math.min(e,r.length);t.enqueue(r.slice(0,s)),e-=s,e===0&&(this.#s(r.slice(s)),t.close())}})}get body(){let e=this.#e.fileSize,t=this.#n.getReader();return new ReadableStream({pull:async r=>{if(e===0){r.close();return}let s=await t.read();if(s.done){r.error(new Error("Unexpected end of stream"));return}let i=s.value,n=Math.min(e,i.length);if(r.enqueue(i.slice(0,n)),e-=n,e===0){for(;!(await t.read()).done;);r.close()}}})}async skip(){let e=this.#n.getReader();for(;!(await e.read()).done;);}async arrayBuffer(){let e=this.body.getReader(),t=new Uint8Array(this.#e.fileSize),r=0;for(;r<t.length;){let s=await e.read();if(s.done)throw new Error("Unexpected end of stream");t.set(s.value,r),r+=s.value.length}return t.buffer}async json(e="utf-8"){let t=await this.text(e);return JSON.parse(t)}async text(e="utf-8"){let t=await this.arrayBuffer();return new TextDecoder(e).decode(t)}};var u=new TextDecoder,h=class{#e;constructor(e){this.#e=e}get name(){let e=this.#e.slice(0,100),t=0;for(let r=0;r<e.length&&e[r]!==0;r++)t=r;return u.decode(e.slice(0,t+1))}get fileSize(){let e=this.#e.slice(124,136);return parseInt(u.decode(e),8)}get checksum(){let e=this.#e.slice(148,156);return parseInt(u.decode(e),8)}};var y=8*32;function g(a){let e=y;for(let t=0;t<512;t++)t>=148&&t<156||(e+=a[t]);return e}async function*v(a){let e=a.getReader(),t=new Uint8Array(512),r=0;for(;;){let{done:s,value:i}=await e.read();if(s)break;let n=i;for(;n.length>0;){let l=512-r,f=n.slice(0,l);if(n=n.slice(l),t.set(f,r),r+=f.length,r===512){let d=new h(t);if(g(t)!==d.checksum)return;let{resolve:w,promise:m}=new o;yield new c(d,e,n,w),d.fileSize>0&&(n=await m),r=0}}}}export{v as entries}; |