├── .gitignore ├── src ├── ffi.js ├── main.gleam └── text.gleam ├── README.md ├── package.json └── bin └── build.js /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | node_modules 3 | -------------------------------------------------------------------------------- /src/ffi.js: -------------------------------------------------------------------------------- 1 | import colors from "colors"; 2 | 3 | // A JavaScript function to be imported by our Gleam code 4 | export function pretty(text) { 5 | return colors.rainbow(text); 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JavaScript support is now built into the Gleam build tool, so this is no longer needed! 2 | 3 | Try running your project with `gleam run --target javascript` or adding `target = "javascript"` to your `gleam.toml`. 4 | -------------------------------------------------------------------------------- /src/main.gleam: -------------------------------------------------------------------------------- 1 | // Import a Gleam module 2 | import text 3 | 4 | // Import a JavaScript function from a JavaScript module 5 | external fn pretty(String) -> String = 6 | "./ffi.js" "pretty" 7 | 8 | // Import a JavaScript function from a global object 9 | external fn log(anything) -> Nil = 10 | "" "console.log" 11 | 12 | // Put it all together 13 | pub fn main() { 14 | log(pretty(text.lucy)) 15 | log(text.greeting) 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gleam_javascript_template", 3 | "type": "module", 4 | "scripts": { 5 | "build": "node bin/build.js build", 6 | "test": "node bin/build.js test", 7 | "start": "node bin/build.js start", 8 | "uuid": "echo 8bde8a39a48c43d494284cacb7198ad1" 9 | }, 10 | "dependencies": { 11 | "colors": "^1.4.0" 12 | }, 13 | "devDependencies": { 14 | "gleam-packages": "file:./target/lib" 15 | }, 16 | "gleamDependencies": [ 17 | { 18 | "name": "gleam_stdlib", 19 | "ref": "main", 20 | "url": "https://github.com/gleam-lang/stdlib.git" 21 | }, 22 | { 23 | "name": "gleam_javascript", 24 | "ref": "main", 25 | "url": "https://github.com/gleam-lang/javascript.git", 26 | "dependencies": [ 27 | "gleam_stdlib" 28 | ] 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/text.gleam: -------------------------------------------------------------------------------- 1 | pub const lucy = " 2 | & 3 | && 4 | &&& &* 5 | #&&% && 6 | &&& &&* 7 | ,&&, , 8 | &&&&&&&&&&&# , 9 | && # 10 | &&&& 11 | ,&&&&&, 12 | &&& && 13 | ,&& && 14 | & 15 | " 16 | 17 | pub const greeting = " ✨ Hello, world! ✨ 18 | 19 | Welcome to Gleam! It's great to have you. 20 | 21 | For more information check out the website: 22 | https://gleam.run/documentation/ 23 | " 24 | -------------------------------------------------------------------------------- /bin/build.js: -------------------------------------------------------------------------------- 1 | // Gleam build.js version:2021-09-12 2 | 3 | import { 4 | rm, 5 | stat, 6 | copyFile, 7 | readFile, 8 | mkdir, 9 | access, 10 | readdir, 11 | } from "fs/promises"; 12 | import { resolve, relative, join } from "path"; 13 | import { promisify } from "util"; 14 | import { exec as callbackExec } from "child_process"; 15 | 16 | let exec = promisify(callbackExec); 17 | 18 | export async function build() { 19 | let { name, gleamDependencies } = JSON.parse( 20 | await readFile("./package.json") 21 | ); 22 | 23 | await Promise.all(gleamDependencies.map(clone)); 24 | for (let dep of gleamDependencies) await cachedBuildProject(dep); 25 | 26 | await buildProject({ 27 | name, 28 | root: ".", 29 | includeTests: true, 30 | dependencies: gleamDependencies.map((d) => d.name), 31 | }); 32 | 33 | return { 34 | name, 35 | }; 36 | } 37 | 38 | async function copyJs(name, dir) { 39 | let inDir = join(dir, "src"); 40 | let out = outDir(name); 41 | let files = await readdir(inDir); 42 | files.map(async (file) => { 43 | if (file.endsWith(".js")) { 44 | await copyFile(join(inDir, file), join(out, file)); 45 | } 46 | }); 47 | } 48 | 49 | async function cachedBuildProject(info) { 50 | if (await fileExists(outDir(info.name))) return; 51 | await buildProject(info); 52 | } 53 | 54 | async function buildProject({ name, root, dependencies, includeTests }) { 55 | console.log(`Building ${name}`); 56 | let dir = root || libraryDir(name); 57 | let src = join(dir, "src"); 58 | let test = join(dir, "test"); 59 | let out = outDir(name); 60 | await rm(out, { recursive: true, force: true }); 61 | try { 62 | await exec( 63 | [ 64 | "gleam compile-package", 65 | `--name ${name}`, 66 | "--target javascript", 67 | `--src ${src}`, 68 | includeTests ? `--test ${test}` : "", 69 | `--out ${out}`, 70 | (dependencies || []).map((dep) => `--lib=${outDir(dep)}`).join(" "), 71 | ].join(" ") 72 | ); 73 | } catch (error) { 74 | console.error(error.stderr); 75 | process.exit(1); 76 | } 77 | await copyJs(name, dir); 78 | } 79 | 80 | async function clone({ name, ref, url }) { 81 | let dir = libraryDir(name); 82 | if (await fileExists(dir)) return; 83 | console.log("Cloning", name); 84 | await mkdir(dir, { recursive: true }); 85 | await exec(`git clone --depth=1 --branch="${ref}" "${url}" "${dir}"`); 86 | } 87 | 88 | function libraryDir(name) { 89 | return resolve(join("target", "deps", name)); 90 | } 91 | 92 | function outDir(name) { 93 | return resolve(join("target", "lib", name)); 94 | } 95 | 96 | async function fileExists(path) { 97 | try { 98 | await access(path); 99 | return true; 100 | } catch { 101 | return false; 102 | } 103 | } 104 | 105 | async function test() { 106 | let gleamPackage = await build(); 107 | 108 | console.log("Running tests..."); 109 | 110 | let dir = `target/lib/${gleamPackage.name}`; 111 | let passes = 0; 112 | let failures = 0; 113 | 114 | for await (let path of await getFiles(dir)) { 115 | if (!path.endsWith("_test.js")) continue; 116 | let module = await import(path); 117 | 118 | for await (let fnName of Object.keys(module)) { 119 | if (!fnName.endsWith("_test")) continue; 120 | try { 121 | await module[fnName](); 122 | process.stdout.write(`\u001b[32m.\u001b[0m`); 123 | passes++; 124 | } catch (error) { 125 | let moduleName = "\n" + relative(dir, path).slice(0, -3); 126 | process.stdout.write(`\n❌ ${moduleName}.${fnName}: ${error}\n`); 127 | failures++; 128 | } 129 | } 130 | } 131 | 132 | console.log(` 133 | 134 | ${passes + failures} tests 135 | ${failures} failures`); 136 | process.exit(failures ? 1 : 0); 137 | } 138 | 139 | async function start() { 140 | let { name } = await build(); 141 | let { main } = await import(join(outDir(name), "main.js")); 142 | return main(); 143 | } 144 | 145 | async function getFiles(dir) { 146 | const subdirs = await readdir(dir); 147 | const files = await Promise.all( 148 | subdirs.map(async (subdir) => { 149 | const res = resolve(dir, subdir); 150 | return (await stat(res)).isDirectory() ? getFiles(res) : res; 151 | }) 152 | ); 153 | return files.reduce((a, f) => a.concat(f), []); 154 | } 155 | 156 | async function main() { 157 | switch (process.argv[process.argv.length - 1]) { 158 | case "build": 159 | return await build(); 160 | 161 | case "test": 162 | return await test(); 163 | 164 | case "start": 165 | return await start(); 166 | 167 | default: 168 | console.error(` 169 | Usage: 170 | node bin/build.js test 171 | node bin/build.js build 172 | node bin/build.js start 173 | `); 174 | process.exit(1); 175 | } 176 | } 177 | 178 | main(); 179 | --------------------------------------------------------------------------------