#Le bac à sable
Un thème est du code, ce code est modifiable par le client, et il s’exécute sur notre serveur — celui qui rend aussi les sites de tous les autres. Une boucle qui s’emballe dans le thème d’un artisan ne doit pas éteindre la boulangerie d’à côté. Cette page dit dans quelles bornes un thème s’exécute, ce qui arrive quand il les dépasse, et ce qui est simplement hors de portée.
Toutes les valeurs ci-dessous sont celles du code, pas des intentions :
src/lib/liquid/moteur.ts pour le rendu, src/lib/liquid/format.ts pour le
format.
#Les trois garde-fous
Ils ne sont pas négociables, et ils ne se remplacent pas l’un l’autre.
Aucun accès au disque. LiquidJS lit normalement les fichiers avec fs. Ici,
le système de fichiers qu’on lui donne est une carte en mémoire : celle des
fichiers du thème, et rien d’autre. render et include ne peuvent donc
atteindre ni /etc/passwd, ni les variables d’environnement, ni le thème d’un
autre client.
Pas de code arbitraire. Liquid n’a ni eval, ni appel de fonction. Ce n’est
pas une restriction qu’on a ajoutée : c’est la raison d’être du langage, et
c’est pour cela que Shopify l’a écrit plutôt que d’exposer un moteur de gabarit
généraliste.
Des bornes. Temps, mémoire, taille de source, profondeur d’imbrication. Sans elles, une boucle sur dix millions d’entiers immobilise le processus, et le processus sert tout le monde.
#Les chiffres
| Borne | Valeur | Où elle s’applique |
|---|---|---|
| Temps de rendu | 3 secondes | Par page, et par section |
| Source analysée | 512 Ko | Par appel de rendu, inclusions comprises |
| Objets créés | 10⁸ | Par rendu |
| Imbrication de sections | 6 niveaux | Tag section uniquement |
| Taille d’un fichier | 512 Ko | À l’écriture comme à l’import |
| Nombre de fichiers | 120 | Par thème |
Les 3 secondes sont appliquées deux fois, et ce n’est pas une redondance inutile. LiquidJS les fait respecter de l’intérieur, y compris au milieu d’une boucle ; une course contre un minuteur les fait respecter de l’extérieur, au cas où un rendu se bloquerait ailleurs que dans Liquid. La première version du moteur n’avait que la seconde — or une course contre un minuteur ne coupe rien quand le rendu est synchrone : la boucle bloquait le fil d’exécution de Node, donc le minuteur ne s’exécutait jamais.
#Ce que « 512 Ko » veut dire exactement
La taille d’un fichier est comparée à la longueur de la chaîne, pas à son
poids en octets — soit 524 288 unités de code UTF-16. Un fichier plein d’accents
et d’emoji passera donc la barre un peu plus tard qu’un ls -l ne le laisse
croire. C’est sans conséquence pratique : aucun fichier de thème sensé n’approche
de cette taille, et la borne existe pour arrêter une archive malveillante, pas
pour discipliner une feuille de style.
Le même plafond sert de parseLimit au moteur, mais il y compte la source
cumulée d’un rendu : une coquille de 100 Ko qui inclut cinq snippets de
100 Ko dépasse, alors qu’aucun fichier pris isolément ne dépassait.
#Ce qui se passe au dépassement
Rien ne tombe en page blanche. C’est un choix, et il se défend : un thème en cours d’édition contient forcément des erreurs passagères, et rendre une page blanche à chaque virgule mal placée rendrait l’édition impossible.
Une section qui échoue est remplacée par un commentaire HTML — invisible pour le visiteur, lisible par qui édite le thème :
<!-- section prestations : Le rendu du thème a pris trop de temps. -->
<!-- sections trop imbriquées à « entete » -->
<!-- section absente : bandeau-promo -->Ces commentaires sont le premier endroit à regarder quand un bloc de page a
disparu sans explication. Ouvre le code source de la page rendue — pas
l’inspecteur, qui ne montre pas toujours les commentaires — et cherche
<!-- section.
#L’imbrication des sections
Le tag section rend une section depuis la coquille. Six niveaux suffisent
largement : au-delà, c’est qu’une section s’appelle elle-même.
<body>
{% section 'entete' %}
{{ content_for_layout }}
{% section 'pied' %}
</body>Le compteur de profondeur vit dans une fermeture, hors de portée du thème. Il a vécu un temps dans le contexte Liquid, ce qui suffisait à le neutraliser :
{% assign _profondeur = 0 %}
{% section 'moi-meme' %}Un compteur qui surveille du code ne doit pas être écrit par ce code. C’est la règle générale, et elle vaut au-delà de ce cas précis.
#Ce qui n’est PAS protégé
Deux points à connaître avant d’écrire, parce qu’ils surprennent.
Liquid n’échappe pas le HTML par défaut. Pas plus chez Shopify. Un thème
peut donc écrire une balise script. C’est sans conséquence tant que le thème
vient de nous ou du propriétaire du site — il ne peut s’attaquer qu’à lui-même.
Le jour où l’on acceptera des thèmes de tiers, il faudra assainir le HTML rendu,
et ce sera une décision à prendre avant d’ouvrir cette porte. Pour du
contenu saisi par le client, utilise escape, ou lignes qui échappe aussi.
Une variable absente rend du vide. Le moteur tourne avec strictVariables
et strictFilters désactivés : une faute de frappe dans un coin de pied de page
ne bloque pas la page entière, elle rend une chaîne vide. Pratique en
production, traître en développement — c’est la première chose à soupçonner
quand un réglage « ne marche pas ».
#Ce qu’un thème ne peut pas contenir
Le dossier détermine les extensions admises. La liste est fermée, et c’est une mesure de sécurité, pas de rangement.
| Dossier | Extensions admises |
|---|---|
layout/ | .liquid |
sections/ | .liquid |
snippets/ | .liquid |
assets/ | .css, .svg |
config/ | .json |
templates/ | .json |
Il n’y a pas de .js dans assets/, et l’absence est délibérée. Un thème
importé est du contenu qui vient de l’extérieur ; un .js déposé dans assets/
serait du script exécuté dans le navigateur de tous les visiteurs du site, sous
son domaine, avec accès à ses cookies. Le JavaScript de thème viendra quand on
saura le relire. En attendant, un thème est du HTML et du CSS.
Un chemin est refusé s’il contient .., s’il commence par une barre oblique,
s’il contient une barre oblique inversée, ou s’il n’a pas exactement deux
segments (dossier/fichier). C’est la faille classique des archives — celle qui
permet d’écrire ailleurs que là où on croit — et elle est vérifiée à
l’entrée, pas au moment d’écrire.
Le nom de fichier lui-même doit commencer par une lettre ou un chiffre, et ne contenir ensuite que des lettres, des chiffres, des points, des tirets et des tirets bas.
#Vérifier avant d’envoyer
Le CLI applique les mêmes bornes que le serveur, en local, avant tout envoi. C’est ce qui évite d’apprendre qu’un fichier est trop gros après un aller-retour réseau.
Compter les fichiers et leur taille
Le CLI refuse de pousser au-delà de 120 fichiers, et signale tout fichier au-dessus de 512 Ko avant de tenter l’écriture.
node scripts/theme.mjs pousser↑ modifié assets/theme.css + nouveau sections/temoignages.liquid ✓ 1 nouveau, 1 modifié, 3 inchangésLire l’erreur de syntaxe, s’il y en a une
Une erreur d’analyse Liquid est signalée avec son fichier, sa ligne et sa colonne, et interrompt l’envoi — un thème à moitié poussé est pire qu’un thème non poussé.
Sortie du CLIbash✗ sections/hero.liquid — tag "for" not closed, line:14, col:1 [syntaxe]Regarder le rendu, pas seulement le code
Les dépassements de bornes ne sont pas des erreurs de syntaxe : ils n’apparaissent qu’au rendu, sous forme de commentaires HTML. Une page qui s’analyse parfaitement peut ne rendre que trois sections sur cinq.
#Les bornes, en un coup d’œil
renderLimitmillisecondesdéfaut : 3000Temps de rendu maximal, appliqué par LiquidJS lui-même — donc y compris au milieu d’une boucle. Au-delà, le rendu est interrompu et la section concernée est remplacée par un commentaire.
parseLimitoctetsdéfaut : 524288Source analysée par appel de rendu, inclusions comprises. Le format autorise déjà moins par fichier ; cette borne plafonne la somme.
memoryLimitobjetsdéfaut : 100000000Nombre d’objets que le rendu peut créer. Une page de site vitrine en fabrique quelques milliers : ce plafond n’arrête qu’une boucle qui s’emballe.
PROFONDEUR_MAXentierdéfaut : 6Niveaux d’imbrication du tag section. Le compteur est hors de portée du thème,
et il est rendu même quand une section échoue.
TAILLE_MAXcaractèresdéfaut : 524288Longueur maximale du contenu d’un fichier, vérifiée à l’import, à l’écriture par l’API, et par le CLI avant l’envoi.
FICHIERS_MAXentierdéfaut : 120Nombre de fichiers d’un thème. L’API refuse la création du 121ᵉ avec
trop_de_fichiers.

