Learn

Webpack e MacOs: import case insensitive

2 Aprile 2020 - 3 minuti di lettura

Tutti gli sviluppatori che utilizzano Mac OS X, compreso me, hanno le loro buone ragioni.

È innegabile però che ci siano alcune differenze di cui va tenuto conto. Una di queste è il fatto che il filesystem è case insensitive.

Facciamo un esempio. Creiamo un file contenente la parola ciao e poi stampiamone il contenuto usando un nome di file con case diverso.
I seguenti comandi non daranno errore per file non trovato ma verrà stampato ciao

echo ciao > FooBar
cat fOObAR

Questa caratteristica può rivelarsi spiacevole specialmente quando si interagisce con sistemi case sensitive, siano essi computer di altri sviluppatori del team o sistemi di build o deploy.

Case insensitive: un esempio concreto

Ho creato una applicazione Node.Js di esempio che sfrutta Webpack per gestire gli import. Nel paragrafo Riferimenti trovate il link al progetto con il codice eseguibile mentre di seguito riportiamo solo le parti principali.
I due file sorgenti dell’esempio sono i seguenti:

index.js

import {foobar} from './FooBar'

console.log("foobar is ", foobar)

Foobar.js

export const foobar = 42;

Da notare che l’import dentro index si riferisce a un file FooBar, mentre il file si chiama Foobar.
La differenza è sottile ed è quello che rende il problema spinoso e difficile da individuare.

Build su Mac Os X

La build può essere eseguita con il comando npx webpack. L’output del comando ne dimostra la corretta esecuzione

Hash: 449c8fcaf39b09803703
Version: webpack 4.42.0
Time: 91ms
Built at: 03/05/2020 7:32:46 PM
  Asset       Size  Chunks             Chunk Names
main.js  980 bytes       0  [emitted]  main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 93 bytes {0} [built]
    | ./src/index.js 67 bytes [built]
    | ./src/FooBar.js 26 bytes [built]

Build tramite docker

Ho approntato anche un dockerfile che esegue una build in un container linux, case sensitive, usando lo stesso comando npx webpack.

Lanciando il comando docker image build -t itFails . vediamo che fallisce per non aver trovato il file giusto.

ERROR in ./src/index.js
Module not found: Error: Can't resolve './FooBar' in '/usr/src/app/src'
 @ ./src/index.js 1:0-31 3:26-32

Case insensitive: cosa possiamo fare

A meno che tutti gli ambienti coinvolti siano case insensitive, è necessario che il casing dei file e dei rispettivi import corrisponda.

La soluzione più semplice è quella di affidarsi alla continuous integration che ci informerà quanto prima della build fallita e avremo così modo di identificare e correggere il problema.

Se però siamo alla ricerca di una soluzione più proattiva, che renda visibile il problema già sull’ambiente di sviluppo in locale, esistono varie possibilità. Di seguito ne cito due, come rappresentanti di due categorie.

Il primo approccio è quello di far fallire la build anche in locale. Questo può essere ottenuto ad esempio con il  plugin di webpack case-sensitive-paths: se il casing dei file importati non corrisponde, il plugin si occuperà di far fallire la build con un errore.

Il secondo approccio è quello di sfruttare eslint configurandolo in modo che evidenzi il problema direttamente nell’ide. Una estensione che fornisce questa funzionalità è eslint-plugin-import che fornisce la regola no-unresolved con l’opzione caseSensitive

Conclusioni

Nello scenario presentato, lo sviluppatore si troverà in una situazione dove il sistema in locale funziona ma fallisce la build di produzione. Il messaggio di errore sembrerà criptico perché ad una prima lettura non si noterà il diverso casing tra il file vero e il nome riportato nell’errore.

Spero che con la lettura di questo articolo l’informazione torni in mente al momento giusto permettendo di evitare inutili perdite di tempo dietro a un errore banale.

Articolo scritto da