DEV Community

Cover image for Bun : Fichiers et répertoires
Sylvain Gougouzian
Sylvain Gougouzian

Posted on

Bun : Fichiers et répertoires

Dans l'article précédent, nous avons découvert Bun et créé un programme qui affiche la version de Bun depuis la commande bunx monprogramme.

Dans cet article, nous allons voir comment Bun simplifie les interactions avec les fichiers.

Lire un fichier

Pour lire un fichier, il faut utiliser la fonction Bun.file().

Cette fonction est synchrone, contrairement à la lecture du contenu qui est asynchrone.

Ainsi, la lecture complète d'un fichier se fait de la manière suivante :

const myFile = Bun.file("./package.json");
const pkg = await myFile.json(); // il existe aussi text(), stream()
console.log(pkg.name); // devrait afficher "monprogramme"
Enter fullscreen mode Exit fullscreen mode

La fonction file() prend en paramètre une chaîne de caractères qui définit le chemin du fichier à traiter et retourne un objet de type BunFile avec 2 propriétés utiles :

  • size : retourne la taille du fichier
  • type : retourne le MIME du fichier
file(path: string | number | URL, options?: { type?: string }): BunFile;
Enter fullscreen mode Exit fullscreen mode

Structure de l'objet BunFile :

interface BunFile {
  readonly size: number;
  readonly type: string;

  text(): Promise<string>;
  stream(): Promise<ReadableStream>;
  arrayBuffer(): Promise<ArrayBuffer>;
  json(): Promise<any>;
  writer(params: { highWaterMark?: number }): FileSink;
}
Enter fullscreen mode Exit fullscreen mode

On voit donc qu'il existe en plus des 2 propriétés, 5 fonctions qui permettent de manipuler le contenu :

  • text() : cette méthode retourne le contenu du fichier sous le format d'une chaîne de caractères ;
  • stream() : cette méthode retourne le contenu dans un objet ReadableStream ;
  • arrayBuffer() : cette méthode retourne le contenu dans un ArrayBuffer ;
  • json() : très utile dans la manipulation de fichier .json, cette méthode prend le contenu du fichier et le transforme en JSON pour faciliter la manipulation de celui-ci.

La méthode writer() est très utile dans la manipulation de Chunk, nous y reviendrons par la suite.

Écrire dans un fichier

Maintenant que nous avons vu comment lire un fichier, nous allons voir comment écrire dans un fichier existant ou non.

write(
  destination: string | number | BunFile | URL,
  input:
    | string
    | Blob
    | ArrayBuffer
    | SharedArrayBuffer
    | TypedArray
    | Response,
): Promise<number>;
Enter fullscreen mode Exit fullscreen mode

Comme on peut le voir sur la définition de la méthode write(), cette méthode retourne une promesse. Elle est donc asynchrone.

Ainsi si nous modifions notre fichier bin/index.js de l'article précédent par :

#!/usr/bin/env bun

const data = `Bun version: ${Bun.version}`;
await Bun.write("output.txt", data);
Enter fullscreen mode Exit fullscreen mode

En lançant la commande bun bin/index.js, nous obtenons un fichier à la racine output.txt contenant :

Bun version: 0.7.3
Enter fullscreen mode Exit fullscreen mode

De ce fait, nous avons vu qu'il est très simple d'écrire une chaîne de caractères dans un fichier non existant.

Comme nous pouvons le constater dans la définition de la méthode, il est possible de passer autre chose qu'une chaîne de caractères, il est possible de passer une Response comme un BunFile.

Par exemple, il est simple de créer la copie d'un fichier :

#!/usr/bin/env bun

const data = `Bun version: ${Bun.version}`;
await Bun.write("output.txt", data);

const input = Bun.file("output.txt");
const output = Bun.file("copy.txt");
await Bun.write(output, input);
Enter fullscreen mode Exit fullscreen mode

En relançant la commande bun bin/index.js ou la commande bunx monprogramme, nous obtenons maitenant 2 fichiers à la racine output.txt et copy.txt contenant tous les deux :

Bun version: 0.7.3
Enter fullscreen mode Exit fullscreen mode

Ecrire un fichier en plusieurs fois pour économiser de la mémoire

Lorsque l'on manipule des fichiers lourds, on peut être parfois limité par la mémoire. Bun propose une solution en utilisant les FileSink.

Comme dit dans le chapitre "Lire un fichier", nous devions revenir sur la méthode writer().

C'est le moment.

Modifions notre fichier bin/index.js par le contenu suivant :

#!/usr/bin/env bun

const file = Bun.file("output.txt");
const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB

writer.write("Première ligne\n");
writer.write("Seconde ligne\n");

writer.flush();

writer.write("Troisième ligne\n");
writer.write("Dernière ligne\n");

writer.end();

writer.unref();
Enter fullscreen mode Exit fullscreen mode

Une fois exécuté avec la commande bun bin/index.js ou la commande bunx monprogramme, nous obtenons le fichier output.txt avec le contenu suivant :

Première ligne
Seconde ligne
Troisième ligne
Dernière ligne
Enter fullscreen mode Exit fullscreen mode

Maintenant essayons de comprendre les lignes que nous avons copiées.

const file = Bun.file("output.txt");
Enter fullscreen mode Exit fullscreen mode

Nous souhaitons enregistrer notre contenu dans le fichier output.txt.

const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB
Enter fullscreen mode Exit fullscreen mode

Nous créons un "buffer" dans lequel nous allons écrire. Ici nous avons précisé un highWaterMark à 1 Mo (1024 Ko, donc 1024 * 1024 octets). Ainsi une fois que le contenu du "buffer" dépasse cette limite, le contenu est écrit dans le fichier, libérant ainsi de la mémoire.

writer.write("Troisième ligne\n");
Enter fullscreen mode Exit fullscreen mode

Cette ligne montre comment ajouter du contenu dans le "buffer".

writer.flush();
Enter fullscreen mode Exit fullscreen mode

La méthode flush() permet d'écrire le contenu du "buffer" dans le fichier et libérer ainsi de la mémoire.

writer.end();
Enter fullscreen mode Exit fullscreen mode

Cette ligne signifie d'écrire le contenu et de finaliser l'écriture.

writer.unref();
Enter fullscreen mode Exit fullscreen mode

Cette ligne est optionnelle, mais très utile pour éviter des fuites de mémoire. On libère le référencement du "buffer". La méthode ref() permet de le référencer à nouveau.

Voilà, maintenant nous avons vu globalement comment écrire dans un fichier.

Répertoires : manipulation

Bun ne dispose pas d'API pour les répertoires (du moins pour la version actuelle lors de l'écriture de cet article).

Nous devons donc utiliser la bibliothèque fs présente dans Node.js.

Modifions notre célèbre fichier bin/index.js dans le dossier monprogramme par le code suivant :

#!/usr/bin/env bun
import { readdirSync } from "fs";

const data = `Bun version: ${Bun.version}`;
await Bun.write("output.txt", data);

const filenames = readdirSync(__dirname);

console.log("\n Binary directory filenames:");
filenames.forEach((file) => {
  console.log(file);
});

const localfilenames = readdirSync(".");

console.log("\nCurrent directory filenames:");
localfilenames.forEach((file) => {
  console.log(file);
});
Enter fullscreen mode Exit fullscreen mode

Nous obtenons le résultat suivant :

$ bunx monprogramme

Binary directory filenames:
index.js

Current directory filenames:
output.txt
Enter fullscreen mode Exit fullscreen mode

Nous avons donc la possibilité d'accèder aux fichiers présents dans le dossier du binaire qu'on execute avec __dirname et également la lecture du dossier d'où le programme s'exécute ".".

Imports

Traditionnellement, les imports se font sur des bibliothèques préalablement installées.

Avec Bun, pas forcément besoin d'installer ces bibliothèques :

import * from "commander"
Enter fullscreen mode Exit fullscreen mode

En écrivant cette ligne, lors de l'exécution Bun va voir s'il connait cette bibliothèque, s'il ne l'a jamais rencontrée, il va la télécharger dans son cache.

Il est également possible de spécifier des versions avec la notation npm.

import * from "commander@11.0.0"
Enter fullscreen mode Exit fullscreen mode

Bon, à part une gestion équivalente à la pnpm, rien de nouveau.

Eh bien si, les imports de fichiers de type json ou toml sont automatiquement convertis en Objet JavaScript.

Modifions notre fichier package.json de monprogramme :

{
  "name": "monprogramme",
  "version": "0.0.1",
  "bin": {
    "monprogramme": "./bin/index.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

Et modifions notre fichier bin/index.js par :

#!/usr/bin/env bun

import pkg from "../package.json";

const data = `version: ${pkg.version}`;
await Bun.write("output.txt", data);
Enter fullscreen mode Exit fullscreen mode

Après une exécution de bunx monprogramme, on obtient un fichier output.txt qui contient :

version: 0.0.1
Enter fullscreen mode Exit fullscreen mode

Un autre avantage de Bun est que si un format de fichier lui est inconnu, il importe le contenu du fichier et le stocke dans une chaîne de caractères.

Imaginons un fichier test.txt avec le contenu suivant :

Ceci est un test
Enter fullscreen mode Exit fullscreen mode

Si nous faisons un import comme l'exemple suivant :

import test from "./test.txt";

console.log(test);
Enter fullscreen mode Exit fullscreen mode

L'exécution du fichier devrait afficher Ceci est un test.

Comment SliDesk utilise les fichiers ?

SliDesk n'écrit pas de fichier, sauf si on utilise l'option --save pour générer des fichiers .html par langue pour les héberger sur Internet.

La lecture de fichier est utilisée pour les fichiers de traductions, et également pour les "assets" (images, fonts, ...) qui doivent être fournis pour la présentation.

L'avantage de Bun sur l'import des fichiers en tant que chaîne de caractères a été très utile. En effet, une volonté de simplification des "layouts" (modèles de fichiers pour le thème, les slides, l'interaction, ...) était voulue pour être des fichiers à part, facilement modifiables.

Dans le fichier core/Interpreter.js :

import layoutHTML from "../templates/layout.html.txt";
import themeCSS from "../templates/theme.css.txt";
import mainJS from "../templates/main.js.txt";
import gamepadJS from "../templates/gamepad.js.txt";
import qrcodeLibJS from "../templates/qrcode.lib.js.txt";
Enter fullscreen mode Exit fullscreen mode

par exemple.

Dans le prochain article, nous verrons comment faire un serveur Web et gérer des WebSockets.

Top comments (0)