A l’heure où l’on parle de micro-services, où on cherche à tout modulariser, et à créer des systèmes à base de dépendances explicites, à ne plus rien mettre dans le scope global
et à utiliser le scope local
uniquement, histoire de maîtriser ce à quoi on a accès et d’éviter des effets de bord : on utilise encore un moteur CSS où on balance tous les sélecteurs à sa racine, qui utilise donc un scope global
. Heureusement, pour ceux qui utilise le shadow DOM, ce problème est résolu. Mais quid de ceux qui ne l’utilise pas ?
Une partie de ce problème peut être évité en adoptant des normes d’écriture et de nommage telle que la norme BEM qui peuvent malgré tout ne pas suffir ou être délicate à utiliser.
Enfin, surtout avec BEM, les noms de classes peuvent être à rallonge, on aimerait bien généraliser le process de minification qu’on utilise sur le contenu des .js
et des .css
, sur le nom des classes elles-même ! Mais cela oblige à modifier d’une part le nom de la classe dans le fichier .css et également le code js qui l’utilise.
Il est même maintenant possible de complétement se passer de fichier .css et d’utiliser uniquement du style inline (quid des performances ?). Par exemple, avec React, le style peut être défini directement dans les composants javascript, et avec des outils tel que Radium on peut même utiliser les sélecteurs spéciaux css tel que :hover
, ou les média queries.
Facebook et le CSS
Facebook a déjà résolu ce problème. Sans doute avez-vous vu la présentation de où il évoque comment Facebook s’assure qu’il n’y a aucun conflit de nom de classe css, que le scope global n’est pas pollué, et que les développeurs peuvent facilement rajouter du style sans avoir peur d’avoir des effets de bord et de modifier le layout quelque part, sans le savoir.
Ils ont étendu le langage css en rajoutant une syntaxe spéciale pour les sélecteurs : button/container
qui ne peut être utilisé que dans le fichier button.css
qui a son tour est référence dans un composant React button.js
, qui enfin, fait référence à className={cx('button/container')}
pour définir la classe d’un élément.
Le process de build vérifie ces références et génére un nom de classe unique à partir de button/container
(qui n’est pas valide en CSS) par quelque-chose comme ._f8z
qui fera parti du scope css global mais qui n’entrera jamais en conflit avec quoi que ce soit vu que le nom est généré aléatoirement (et est unique), personne ne pouvant le deviner à l’avance. Tout le monde doit donc utiliser ce système pour travailler et styler son contenu.
Moi aussi j’aimerai faire ça. Ca tombe bien, webpack est là.
webpack et css-loader
webpack, et en particulier le plugin css-loader combiné à extract-text-webpack-plugin, permet de former un (ou des) bundle css à partir de fichiers .css
.less
ou .sass
(avec les loaders qui vont bien) qui sont eux-même importés dans des fichiers .js :
import React from 'react'; import './App.less'; export default class extends React.Component { render() { return } }
Avec une configuration webpack de ce genre :
var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { entry: './src/app.js', output: { path: __dirname + '/dist', filename: 'bundle.js' }, loaders: [ { test: /\.js$/, exclude: /node_modules/, loaders: [ 'babel-loader' ], }, { // we can do: import './Component.css'; test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') }, { // we can do: import './Component.less'; test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader!less-loader') } ] }, plugins: [ new ExtractTextPlugin('[name].css') ] };
Ce build permet de créer un bundle css qui contiendra tout le contenu référencé par les imports de fichiers .css
ou .less
dans les fichiers javascript.
import './App.less';
css-loader local scope
La nouveauté (d’avril 2015) est une nouvelle syntaxe au niveau des fichiers css pas encore transformés, mais prise en compte par css-loader
:
:local(.container) { font-size: 30px; }
Se transforme en :
._3ImWIJ65ktg-PxiyA_aFIC { font-size: 30px; }
Cela signifie que vous ne pouvez plus utiliser simplement className="container"
, cette classe css n’existe plus. Et vous ne pouvez pas mettre className="_3ImWIJ65ktg-PxiyA_aFIC"
quand même ! Il faut importer différemment le fichier .css
ou .less
:
import AppStyles from './App.less'; ...Les classes définies dans le fichier css seront accessibles via leur nom (si
:local(.my-class)
alors viamy-class
) dans l’objet importé, qu’on peut alors utiliser pour indiquer laclassName
. Au niveau HTML, on aura ce rendu :Autre exemple avec un autre sélecteur css à l’intérieur :
:local(.container) { font-size: 31px; span { letter-spacing: 5px; } }Cela génère bien :
._3ImWIJ65ktg-PxiyA_aFIC { font-size: 31px; } ._3ImWIJ65ktg-PxiyA_aFIC span { letter-spacing: 5px; }Au niveau du bundle
AppStyles
est en fait simplement un dictionnaire généré et injecté à la volée de la forme :module.exports = { "container":"_3ImWIJ65ktg-PxiyA_aFIC" };className et pas style
Attention à bien utiliser
className={ AppStyle.container }
et passtyle={ AppStyle.container }
sans quoi l’erreur suivante se produirait :Uncaught Error: Invariant Violation: The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.Le nom des classes générées
On peut modifier la manière dont le nom des classes sont générés. Par défaut, il s’agit d’un hash comme on peut voir, mais on peut le modifier de la sorte (via webpack.config.js) :
css-loader?localIdentName=[hash:base64] _3ImWIJ65ktg-PxiyA_aFICcss-loader?localIdentName=[path]-[name]-[local]-[hash:base64:5] .src--App-container-3ImWI
path:
le path du fichier javascriptname:
le nom du fichier javascriptlocal:
le nom utilisé dans le fichier css:local(.container)
Il n’y a malheureusement pas de méthodes (pour l’instant) pour générer des noms du genre
._a
,._b
, …._az
histoire de rester ultra court et unique (étant un simple compteur).La gestion de cet identifiant provient du module webpack loader-utils.
Plusieurs imports
Si un fichier Javascript dépend de plusieurs fichiers de styles et qu’un, il faut simplement concaténer le nom des classes provenant des 2 imports :
import AppStyle from './App.less'; import GlobalStyle from './Global.less'; ... className={GlobalStyle.container + ' ' + AppStyle.container}Plus loin
D’autres fonctionnalités sont disponibles, comme l’héritage de règles ou la définition de règle globale via
:global
, je vous conseille donc d’aller lire la page du projet : https://github.com/webpack/css-loaderTout cela est encore récent et en développement (allant même à provoquer des breaking changes, des changements de syntaxe), ce qui la rend instable à utiliser pour l’instant, attention!
Docker sous Windows 8.1
Après avoir joué un peu avec boot2docker il y a quelques temps pour pouvoir tester Docker et lancer quelques hello-world, je suis tombé sur une news comme quoi une équipe avait réussi à *le* faire compiler sous Windows ! Il était possible de le récupérer via chocolatey et de l’installer simplement et magiquement à la Unix :
choco install docker
.
*Le* : comme *Le* client Docker, pas le moteur de conteneurs Docker qui lui, est encore exclusivement disponible sous un environnement Linux. Le client ne fait que se connecter à ce moteur. Sous Windows, il faut donc toujours une machine Linux quelque part faisant tourner le moteur de conteneur Docker : c’est ce quoi à sert boot2docker. Il installe en fait virtualbox et y créé une machine virtuelle :
Normalement, rien ne sert d’ouvrir VirtualBox pour jouer avec la VM. Les commandes de boot2docker s’en occupe:
init Create a new Boot2Docker VM. up|start|boot Start VM from any states. ssh [ssh-command] Login to VM via SSH. save|suspend Suspend VM and save state to disk. down|stop|halt Gracefully shutdown the VM. restart Gracefully reboot the VM. poweroff Forcefully power off the VM (may corrupt disk image). reset Forcefully power cycle the VM (may corrupt disk image). delete|destroy Delete Boot2Docker VM and its disk image. config|cfg Show selected profile file settings. info Display detailed information of VM. ip Display the IP address of the VM's Host-only network. shellinit Display the shell commands to set up the Docker client. status Display current state of VM. download Download Boot2Docker ISO image. upgrade Upgrade the Boot2Docker ISO image (restart if running). version Display version information.Les principales commandes étant : init (créer la VM), start/stop, ssh (se connecter à la VM pour avoir accès à son shell), upgrade (à faire de temps en temps pour mettre à jour la VM si mise à jour il y a).
Pour rappel, installer le client de chocolatey n’est pas requis pour utiliser Docker sous Windows. Il y a déjà un client Docker dans la VM installé par boot2docker. Il suffit de s’y connecter via
boot2docker ssh
et d’utiliser les commandes$ docker run hello-world
.
Le client de chocolatey permet de se passer de cette étape, et d’utiliser les commandes docker directement sous votre shell Windows (cmd, powershell, cmder..).Client docker => docker engine (boot2docker VM)
On commence par s’assurer que la VM de boot2docker est en train de tourner.
> boot2docker up Waiting for VM and Docker daemon to start... ............................ooooooooooooooooooooooo Started. Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\ca.pem Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\cert.pem Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\key.pem To connect the Docker client to the Docker daemon, please set: export DOCKER_HOST=tcp:// export DOCKER_CERT_PATH='C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm' export DOCKER_TLS_VERIFY=1Une fois up, il vous exécuter les lignes indiquées pour que le client Chocolatey puisse se connecter au daemon Docker sur la VM.
Si vous n’exporter pas ces variables, vous rencontrez ce genre d’erreur :
> docker run hello-world FATA[0000] Post http:///var/run/docker.sock/v1.18/containers/create: dial unix /var/run/docker.sock: An address incompatible with the requested protocol was used.. Are you trying to connect to a TLS-enabled daemon without TLS?En gros, cela permet au client Docker de savoir où est la VM via son IP et où est le certificat pour s’y connecter via TLS.
Vous pouvez revoir ces variables via la commandeshellinit
:> boot2docker shellinit Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\ca.pem Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\cert.pem Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\key.pem export DOCKER_HOST=tcp:// export DOCKER_CERT_PATH='C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm' export DOCKER_TLS_VERIFY=1En mode Windows, cela donne :
> set DOCKER_HOST=tcp:// > set DOCKER_CERT_PATH=C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm > set DOCKER_TLS_VERIFY=1Une fois cela fait, un petit test histoire de voir que tout va bien :
> docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from hello-world a8219747be10: Pull complete ================================================>] 596 B/596 BB 91c95931e552: Already exists ===============================================>] 32 B/32 BB hello-world:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security. Digest: sha256:aa03e5d0d5553b4c3473e89c8619cf79df368babd18681cf5daeb82aab55838d Status: Downloaded newer image for hello-world:latest Hello from Docker. This message shows that your installation appears to be working correctly ...N’oubliez pas de donner une valeur à ces variables au démarrage de votre shell à chaque fois, via un .bashrc si vous avez, ou via les variables d’environnement Windows :
Les erreurs possibles
Si, quand vous lancer la VM dans VirtualBox, si vous obtenez cette erreur :
VT-x is not available (VERR_VMX_NO_VMX)
Ou si en utilisant boot2docker, vous obtenez, peu importe la commande, cette erreur :
error in run: Failed to start machine "boot2docker-vm": exit status 1Vérifiez que Hyper-V ne soit PAS activé dans vos Windows Features, sinon désactivez-le, et redémarrer votre Windows pour que cela soit pris en compte.
Plus rarement, car toujours activé de nos jours, et que je n’ai pas testé, j’ai également lu qu’il faut s’assurer que la virtualization VT-x soit activée dans votre BIOS (généralement dans un menu Advanced > CPU).
Si vous avez des erreurs bizarre comme quoi la VM n’existe pas alors qu’elle est bien là :
> boot2docker -v ssh Boot2Docker-cli version: v1.6.2 Git commit: cb2c3bc 2015/05/17 23:01:24 executing: C:\Program Files\Oracle\VirtualBox\VBoxManage.exe showvminfo boot2docker-vm --machinereadable error in run: Failed to get machine "boot2docker-vm": machine does not exist (Did you run `boot2docker init`?) > boot2docker init error in run: Failed to initialize machine "boot2docker-vm": exit status 1Pourtant, la VM est bien là !
> ls "VirtualBox VMs\boot2docker-vm\ Logs boot2docker-vm.vbox boot2docker-vm.vbox-prev boot2docker-vm.vmdkN’oubliez pas de lancer votre shell ou votre gestionnaire VirtualBox en mode administrateur ! Testé et approuvé.
Using react-hot-loader with a webpack-dev-server and a node server
2015-12-31: You should check out my recent post to use the new way of doing HR with webpack, and to have way more details :
Webpack Hot Reloading and React : how ?Don’t work without hot-reloading
I got a project running its own Node server instance, using expressjs. It’s already bundling its assets with webpack, and it’s using React with the nice ES6 features, all good.
But it lacks of hot reloading. :-(
This is something we must have nowadays when you’re working with webapps and CSS/Javascript. You don’t want to refresh your whole page and lose your current state each time, you are going to lose your time, your patience, and your productivity !
Leave express alone and add webpack-dev-server on top
Thus, I decided to take a look and tried to add it while keeping the express server instance up and working. I didn’t want to replace the whole thing by a webpack-dev-server and reconfigure the public assets paths and all other stuff that my expressjs was providing.
After some tests, some readings here and there, I got a pretty straightforward solution without using any custom proxy code, nor changing my generated html.
The solution
For reference:
– the expressjs server is running onlocalhost:3000
– the webpack-dev-server is going to run onlocalhost:3001
Here is a git diff of my changes:
packages.json
+ "react-hot-loader": "^1.2.7", + "webpack-dev-server": "^1.8.2"Just the necessary packages to do the hot reloading. (
npm install --save-dev
)webpack.config.js
+var webpack = require('webpack'); - entry: path.resolve(__dirname, '../src/client.js'), + entry: [ + 'webpack-dev-server/client?http://0.0.0.0:3001', + 'webpack/hot/only-dev-server', + path.resolve(__dirname, '../src/client.js') + ], modules: { loaders: [ { - loader: 'babel-loader' + loaders: [ 'react-hot-loader', 'babel-loader' ] } ] }, + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new webpack.NoErrorsPlugin() + ]– we add webpack-dev-server entries into entry
– we add thereact-hot-loader
loader when processing the .js (it will add some js to magically do the hot reloading)
– we add the plugins for hot reloading.More details here http://gaearon.github.io/react-hot-loader/getstarted/
server.js
This is the most important part, where we define a new
webpack-dev-server
instance that will be listen by the browser to process the hot reloading if any js file change.// existing express server var app = express(); app.use(...) app.get(...) app.listen(3000); +// we start a webpack-dev-server with our config +var webpack = require('webpack'); +var WebpackDevServer = require('webpack-dev-server'); +var config = require('./webpack.config.js'); +new WebpackDevServer(webpack(config), { + hot: true, + historyApiFallback: true, + proxy: { + "*": "http://localhost:3000" + } +}).listen(3001, 'localhost', function (err, result) { + if (err) { + console.log(err); + } + + console.log('Listening at localhost:3001'); +});We are only adding a new
webpack-dev-server
instance without altering at all our expressjs server listening onlocalhost:3000
.– our
webpack-dev-server
listens tolocalhost:3001
and handle thehot
ness
– every requests (“*”) are redirected tolocalhost:3000
.This server serves only as a proxy for the hot reloading. More details here http://webpack.github.io/docs/webpack-dev-server.html.
At the end, there is still no hot reloading on
http://localhost:3000
(it’s still a pure expressjs server).But if you browse
http://localhost:3001
, and more preciselyhttp://localhost:3001/webpack-dev-server/
, you will the famous “App ready” hot reloading toolbar and everything will be automatically updated if you change one of your React component.Then, suddenly
It’s been a while.
I couldn’t sleep. I was thinking, maybe too much.
bookmarks.findAll();
I love IT, I love new technos, I love to discover new things and try to use them in the best way possible. I love to read stuff, blogs, twitter, I follow every links I find and read about everything. I want to know everything.
I have something like 100 unread bookmarks (I’m reading a bit every day but finding new ones at the same time), and the export file of all of them stands at 1800 lines, wow.
time.stop();
Discussing last night with a friend of mine who is quite successful — he’s working on different interesting projects on its own or with a team, knows a lot of tools/technos/languages, use them properly in a performant way, knows what he wants, and how to evaluate the stuff he’s working with — made me realize that I want the same, but I’m stuck.
I’m not more stupid than he is, why couldn’t I ? what is blocking me ?
I’ve started BASIC at 10 and has always been some kind of reference for my friends/collegues when it comes to languages, patterns, ideas.
Thus this long night, then this post. Why should I post something and not move on, right now ? I guess it’s just to recap my own thinking.
require(‘experiment’);
So I’m reading a lot, and as I was tolding him, I can’t practice everything I read, there is way too much.
I already barely have time to read what I want and bookmark new stuff, and do things with my wife, how could I experiment those new tools, technos, languages I’m reading about ?
For some of them, I could, because it’s not that complicated to use at first glance such as the Javascript framework and tools. You don’t need a lot except NodeJS most of the time, install some modules and you can give a try.
When it comes to tools, that’s sometimes more difficult. For instance, I didn’t test “Flow” yet (the static typechecker from facebook) because I’m running on Windows and there is no official binaries yet. When you don’t have a Mac or a Linux, that can slow you down in your progress. (oh and I have a Windows Phone since few years, I’m definitely thinking to abandon Microsoft there, I can’t support to never have the application I want. It’s often Apple Store only).
I didn’t test every database or server I’m reading about. I don’t have time ! Or I just realize that it’s just useless to install something, do the Hello World, then be done. That’s useless, and I will forget about it in a few days.
If I don’t have a project using it, I just don’t see the point.
I just want to remember the name, what it does, what is its strength, “in case of”.
But I realize that I often forget things I’ve read one month ago if I didn’t practice. Even with you experiment something, you come there one month later, I’m way more interrogative about what should I type, where should I go, and always need to refer to Google or my bookmarks, or lose time trying stuff until I find the good combinaison.
But sometimes, things stays, such Sublime Text. It was a awesome discovery few months ago and I’m still using it. I’m not mastering every shortcuts, just the one I need and can think of, I know there are a bunch of them that could often help me, it’s just that I don’t realize. I guess with the experience, that will come in handy.
Few month ago, I watched a video of a guy coding an algorithm (in NodeJS, doing TDD). I was amazed by the speed of the development and the tools he was using : build the skeleton using yo, quite fast at using VIM with a lot of shortcuts, install what was needed to run the tests, auto-run them, then finally writing the algorithm etc. For some, this can seems like “why the fuck should I watch a random guy coding”, well, it was actually very very interesting to see how someone else is doing, you can learn so much.
We need you !
- I want to do some big data work with Hadoop, Cassandra, HBase, CouchDB.
- I want to run some true queue tasks system such as ZeroMQ, Apache Apollo, RabbitMQ.
- I want to use some virtual machines and containers (in a performant, secure, reliable and proper way! not just install/run and be done!).
- I want to use some scalable systems, distributed databases, see how it reacts.
- I want to use graph database such as Neo4J and do some ElasticSearch.
- I want to work on a isomorphic React website using ES6.
- I want to get rid of WordPress and write my own performant React blog with multilanguage support.
- I want to use Redis, I barely test it and I know its potential is huge.
- I want to use Varnish, HAProxy, I know they are the way to go for web performance.
- I want to use admin and deployment tools such as Vagrant, Chef, Puppet, Ansible.
- I want to use the cloud as it should (Amazon, Azure), never did.
- I want to work using GitHub with a team, and work the proper way (branches, rebase).
- I want to use Slack
- I want to work with nice work and build processes (hot reloading, gulp/webpack).
- I want to do some mobile native app (and React Native ofc).
- I want to work with TDD, I don’t like bugs.
- I want to work with performant tools, and be able to profile anything. I don’t like to wait, and I love performances.
I want to masterize some tools. I don’t masterize anything right now. I know “well” React, I’ve subcribed to the github repo and I’m reading every emails I got but I’m starting to feel that it’s just useless. I’ve started some side projects but never finished them. At work, I’m using the classic .NET framework, and basic SQL, no fancy stuff. I know them “well” but that’s it. I think I’m getting lazy of all that. I want to work on new stuff, new tools, new ways of thinking.
volatile-ttl
Doing a introspection of myself, I realize that :
- my memory sucks
- if I don’t work on something during some times, I will forget about it. (well, it will go into my bookmarks at least!)
- I don’t know how to evaluate if a tool or framework is good
- I’m interesting in everything, I should filter what I know I won’t use
- I know of lot of names (tools, frameworks) but can’t
- And still missing important ones (I just heard about Apache Apollo, Jenkins)
- I’m not mastering anything
- I love to learn stuff but don’t practise enough
- I like to write
- I like order, performance, consistency, clean stuff
- I like money, I want passive income
- I can’t stay with one tool/one framework, I’m always attracted with the latest and new one
- I don’t have a lot of free time: need to optimize how I’m thinking, how I’m working
- I should unsubscribe from React mailing list
- I should truly install Linux or well, buy a MacBook Pro and the iPhone with it.
Okay, this post is a bit messy but this is what was on my mind.
Now, listening to Global Communication to relax (thanks to some reddit post I stumbled upon!).