[≡]
🕑 7 min

Como hospedar Progressive Web Apps no GitHub Pages

Eu vivo falando da rede social de banco de dados em tempo real chamada Firebase neste blogue digital. O Firebase ja é conhecido pela suas funcionalidades de Hosting, e basta um firebase deploy para por um projeto no ar. Porém, como eu sou uma pessoa chata e desagradável, eu não gosto muito de usar o Firebase Hosting quando eu não estou usando nada além do própiro Firebase Hosting.

Explico: quando estamos usando mesmo o Firebase, aí tudo bem, estamos tudo em casa, tem o Database, tem o Hosting, tem algum serviço de autenticação… se justifica deixar tudo lá. Mas quando eu não tô usando outra coisa sem ser o Hosting, aparecem certas coisas que me incomodam: ter que criar um projeto novo no Firebase (que são limitados, caso não saiba), ter que inicializar o repositório com as configurações, usar o Firebase-CLI além de – obviamente – o próprio Git, e configurar o domínio e SSL não é difícil; mas é chato, demorado e manual (ao menos pros meus padrões).

Quando essa situação acontece, eu prefiro usar o GitHub Pages para por minhas páginas estáticas no ar. Eu já ia estar usando o Git de todo jeito, agora eu só tenho que dar push na branch certa e ele vai estar no ar. Nesses dias rolou algo interessante, um problema que eu nunca tinha enfrentado antes. Quando a gente desenvolve um Progressive Web App, é normal ele ter ao menos um tipo de autenticaçãozinha que seja. Então, eu sempre hospedava já no Firebase.

O Firebase Hosting tem um tipo de configuração especial para Single Page Apps: como é sabido, single page apps tem só o index.html e todo o conteúdo é adicionado e removido do DOM, e mudamos os valores e URL via JavaScript. No Firebase podemos configurar para que todos os requests válidos sejam redirecionados para o index, e assim é garantido que o usuário acessará o conteúdo devido, mesmo fazendo um acesso direto a uma página específica sem ser a própria home. É só por isso aqui no seu firebase.json:

"rewrites": [ {
  "source": "**",
  "destination": "/index.html"
} ]

Ou seja, quando acessamos algum SPA hospedado no Firebase por: https://meu.pwa/pagina, o Firebase vai redirecionar o request para index.html e – se você fez tudo certo – vai receber de parâmetro “pagina” e você redireciona o usuário pro conteúdo devido.

Mas no GitHub Pages, se eu tentar acessar https://meu.pwa/pagina, o que acontece? Isso mesmo:

Print Screen da página de 404 do GitHub

O famoso erro 404. O GitHub quando você tenta acessar https://meu.pwa/pagina ele tenta retornar https://meu.pwa/pagina/index.html. Como essa página não existe, ele retorna a página de erro 404. Se você parar pra pensar, realmente o servidor está coberto de razão.

Eu podia nesse momento engolir o orgulho e voltar chorando pr’os braços do Firebase. Mas eu sou Brasileiro e não desisto nunca. Algum jeito tem que ter, não é possível. E como em 99,9% dos casos, tem solução pra tudo.

Achei o repositório do ilutre desconhecido @rafrex. Eu podia terminar o post aqui linkando o repositório, mas lá tem tanto arquivo, tanto texto, tanta coisa ~desnecesária (apesar de entender porquê eles estão lá) que vou pular a abertura da série pra você e explicar o que tem que ser feito.

No Firebase, ele redireciona todos os requests pra index.html. No GItHub não. Mas se você parar pra pensar, dado que é um SPA. Só existem duas possibilidades de acesso direto a arquivos:

– O próprio index.html

– A página de erro 404

Aqui que a mágica acontece. Tecnicamente falando, o GitHub faz a mesma coisa que o Firebase, só que ao invés do index.html ele sempre redireciona para 404.html. Se a gente der um jeito de redirecionar o 404 para o index e passar os parâmetros, estamos feitos, tudo funciona. Então, a gente tem que fazer duas coisas. Primeiro, escrever isso aqui no código da nossa 404.html:

<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <script type="text/javascript">
      // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
      var pathPrefix = false;
      var l = window.location;
      l.replace(
        l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
        l.pathname.split('/').slice(0, 2 * pathPrefix).join('/') + '/?p=/' +
        l.pathname.slice(1).split('/').slice(pathPrefix).join('/').replace(/&/g, '~and~') +
        (l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
        l.hash
      );
    </script>
  </head>
  <body>
  </body>
</html>

O código é simples mas genial. Se o usuário acessa https://meu.pwa/pagina, o GitHub renderiza o template de 404.html. No próprio template, ele pega a própria url e transforma https://meu.pwa/pagina em https://meu.pwa/?p=pagina. Aí agora a história é outra. Estamos indo para index.html, que é uma página válida. Então lá, precisamos adicionar o seguinte código:

<script type="text/javascript">
 // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
    (function(l) {
        if (l.search) {
          var q = {};
          l.search.slice(1).split('&').forEach(function(v) {
            var a = v.split('=');
            q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&');
          });
          if (q.p !== undefined) {
            window.history.replaceState(null, null,
              l.pathname.slice(0, -1) + (q.p || '') +
              (q.q ? ('?' + q.q) : '') +
              l.hash
            );
          }
        }
    }(window.location))
</script>

Aqui termina o truque. Como esse problema de URLS só acontece quando estamos trabalhando diretamente com a History API, o nosso próprio App vai usar a própria History API, independente do Framework que você estiver usando. Aí é garantido que a página vai ser redirecionada para o conteúdo esperado.

Aí é só correr pro abraço. Se você quisser ver um exemplo com Polymer funcionando, pode acessar o website web online do I/O Extended Pernambuco. Foi justamente por culpa dele que eu descobri como que isso acontece.

Nada como ser cabeça dura ás vezes, né não?

Recadinhos 🔥
  • [PWA Press] Conheça meu novo Newsletter quinzenal sobre Progressive Web Apps!
  • [Google Launchpad Build Recife] Vai ter talk minha no Launchpad Build Recife sobre Progressive Web Apps, inscreva-se grátis!
  • [Updates no blog] Dei uma tapa no visual com umas fontes mais bonitinhas, melhor responsividade e o melhor domínio possivel
  • [JOSE2] Inscreva-se no meu canal no YouTube
  • [Josebot] Curta minha fanpage e fale com meu bot no Facebook