Skip to content

Instantly share code, notes, and snippets.

@ninetails
Last active February 17, 2020 19:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ninetails/2103bc4496c25ec6debe62b6a1dd6153 to your computer and use it in GitHub Desktop.
Save ninetails/2103bc4496c25ec6debe62b6a1dd6153 to your computer and use it in GitHub Desktop.
Talks DFT

Coding Dojo


Dojo


Dojo é o local onde se treinam artes marciais japonesas.


Lugar de iluminação


namaste


Formatos


Kata


Alguém apresenta uma solução pronta previamente desenvolvida e que utilizou algum tipo de teste


Randori


howto


  • Não trazer problemas de fora
  • É um local de treinamento
  • Não necessariamente precisa ser na linguagem que todos estão acostumados
  • Inclusivo

Agenda

  • Apresentação de 1 a 2 Katas
  • Restante do tempo focar no Randori
  • Retrospectiva

  • Não é necessário completar o problema
  • Estamos todos para treinar... DOJO

Extra


lembrete


Baby Steps


kids


Exemplo simples: Fatorial


describe('fatorial', () => {
  test('0! = 1', () => expect(factorial(0)).toBe(1))
})

FAIL!


function factorial (num) {
  return 1
}

PASS!


describe('fatorial', () => {
  test('0! = 1', () => expect(factorial(0)).toBe(1))
  test('1! = 1', () => expect(factorial(1)).toBe(1))
})

PASS!


function factorial (num) {
  return 1
}

describe('fatorial', () => {
  test('0! = 1', () => expect(factorial(0)).toBe(1))
  test('1! = 1', () => expect(factorial(1)).toBe(1))
  test('2! = 2', () => expect(factorial(2)).toBe(2))
})

FAIL!


function factorial (num) {
  if (num < 2) {
    return 1
  }

  return 2
}

PASS!


describe('fatorial', () => {
  test('0! = 1', () => expect(factorial(0)).toBe(1))
  test('1! = 1', () => expect(factorial(1)).toBe(1))
  test('2! = 2', () => expect(factorial(2)).toBe(2))
  test('3! = 6', () => expect(factorial(3)).toBe(6))
})

FAIL...


Fazer mais um if?


Acabei de ganhar 3 testes na minha suite!


Break dance


Referências


Wikipedia

https://pt.wikipedia.org/wiki/Coding_Dojo


http://codingdojo.org/


Nosso primeiro problema


(vamos pular o primeiro Kata)


Problema: FizzBuzz

Linguagem: JavaScript

Reutilização de código


Design Patterns


Christopher Alexander

Christopher Alexander


1977


  • Encapsulamento
  • Equilíbrio
  • Abstração
  • Abertura
  • Combinatoriedade

1987


Kent Beck

Kent Beck


Ward Cunningham

Ward Cunningham


Design Patterns


Solução generalista para problemas corriqueiros em arquitetura de Software


Javeiros


Refactoring

Martin Fowler


Gang of Four

Gang of Four

  • Erich Gamma
  • Richard Helm
  • Ralph Johnson
  • John Vlissides

Design Patterns


  • Programe uma interface, não uma implementação
  • Composition over inheritance

Preciso decorar?


Mas e pro React?


https://pt-br.reactjs.org/docs/design-principles.html


  • Composição
  • Abstração

Componentização


Design Patterns


https://reactpatterns.com/


  • Function component
  • Destructuring props
  • JSX spread attributes
  • Merge destructured props with other values
  • Conditional rendering
  • Children types
  • Array as children
  • Function as children
  • Render prop
  • Children pass-through
  • Proxy component
  • Style component
  • Event switch
  • Layout component
  • Container component
  • Higher-order component
  • State hoisting
  • Controlled input

Preciso decorar?


Uma listinha legal


Lembretes importantes:


DRY

Don't Repeat Yourself


KISS

Keep It Simple...


Clean Code

  • Formatação Horizontal
  • Formatação Vertical

(bom senso)


JS(X)?


Sintaxe docinha


function Component ({ children, ...props }) {
  return <div {...props}>{children}</div>
}

function Component ({ children, ...props }) {
  return React.createElement('div', props, children)
}

function Component ({ children, ...props }) {
  return <OtroComponente {...props}>{children}</OtroComponente>
}

function Component ({ children, ...props }) {
  return React.createElement(OtroComponente, props, children)
}

React.createElement


render wat

wat


function Component () {
  return 'string'
}

function Component () {
  return <OtroComponente />
}

function Component () {
  return null
}

function Component () {
  return [
    'a',
    999,
    <OtroComponente key='🔑' />
  ]
}

function Component ({ items }) {
  return (
    <div>
      {
        items.map((item, index) =>
          <span key={`item-{index}`}>{item}</span>)
      }
    </div>
  )
}

Função x Atribuição?


function Component () {}

// vs

const Component = () => // ...

Pessoal


Arrow Function são funções anônimas


No display name


Usar função quando não tiver atribuição

const Button = styled.button`
  /* ... */
`

Button.displayName = 'styled(Button)'
// ou simplesmente
Button.displayName = 'Button'

Smart vs Dumb Components


Presentational and Container Components


(no geral) Dumb Components são fáceis de testar e de visualizar no Storybook


defaultProps


function Greeting({ name }) {
  return <div>Hi {name}!</div>
}

function Greeting({ name = 'Kazuo' }) {
  return <div>Hi {name}!</div>
}

function Greeting({ name }) {
  return <div>Hi {name}!</div>
}

Greeting.defaultProps = {
  name: 'Kazuo'
}

shouldComponentUpdate


class MyComponent extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    // props
    if (this.props.name !== nextProps.name) {
      return false
    }

    // state
    if (this.state.name !== nextState.name) {
      return false
    }
  }

  render () {
    // ...
  }
}

class MyComponent extends PureComponent {
  render () {
    // ...
  }
}

Ele faz uma comparação juntos e shallow


const actualProps = {
  name: 'foo',
  obj: {
    bar: 'baz'
  }
}

const nextProps = {
  name: 'fooz',
  obj: {
    bar: 'bazes'
  }
}

Imutabilidade
const nextProps = {
  ...actualProps,
  obj: {
    ...actualProps.obj,
    bar: 'bazes'
  }
}

const nextProps = {
  name: 'fooz',
  obj: {
    bar: 'bazes'
  }
}

Componentes Funcionais

React.memo


function MyComponent (props) {
  /* renderize usando props */
}
function areEqual(prevProps, nextProps) {
  /*
  se prevProps e nextProps renderizam o mesmo resultado,
  retorne true.
  caso contrário, retorne false.
  */
}
export default React.memo(MyComponent, areEqual);

Só consigo comparar props


function Greeting({ name = 'Kazuo' }) {
  return <div>Hi {name}!</div>
}

function Greeting({ name }) {
  return <div>Hi {name}!</div>
}

Greeting.defaultProps = {
  name: 'Kazuo'
}

Spread attributes


function Greeting({ name, ...restProps }) {
  return <MyDiv {...restProps}>Hi {name}!</MyDiv>;
}

E os propTypes?


Greeting.propTypes = {
  name: PropTypes.string.isRequired
}

Greeting.propTypes = {
  ...MyDiv.propTypes,
  name: PropTypes.string.isRequired
}

Bom senso


Condicionais


function Component ({ vaiChover, diaDeBanho, calor }) {
  return (
    <div>
      {!vaiChover && diaDeBanho && calor && <Chuveiro />}
    </div>
  )
}

function Component ({ vaiChover, diaDeBanho, calor }) {
  const shouldIDisplayChuveiro = !vaiChover && diaDeBanho && calor
  return (
    <div>
      {shouldIDisplayChuveiro && <Chuveiro />}
    </div>
  )
}

Lembra que vc pode retornar null?


function Chuveiro ({ vaiChover, diaDeBanho, calor }) {
  if (vaiChover) return null
  if (!diaDeBanho) return null
  if (!calor) return null

  return 🚿
}

function Component (props) {
  return (
    <div>
      <Chuveiro {...props} />
    </div>
  )
}

function Component (props) {
  return (
    <div>
      {
        isSegunda
        ? 'segunda'
        : isTerca
          ? 'terca'
          : isQuarta
            ? // ...
      }
    </div>
  )
}

Bom senso

  • Um ternario
  • Multiplos? PFVNOES!


<button type="button">

const Button = props =>
  <button type="button" {...props}>

Context API

const MyContext = React.createContext(defaultValue)

// Provider
<MyContext.Provider value={/* some value */}>

// Consumer
<MyContext.Consumer>
  {value => /* renderiza algo baseado no valor do context */}
</MyContext.Consumer>

AppBanner

import AppBanner from '@dafiti/components/dist/modules/AppBanner'

<AppBanner>
  <AppBanner.CloseButton>Ignorar esta notificação por 24 horas.</AppBanner.CloseButton>
  <AppBanner.Logo
    src="https://dafitistatic-a.akamaihd.net/dynamic_yield/cms/static/images/2f095a5cf831f__icn_dafiti_mobile.jpg"
    alt="Logo do aplicativo da Dafiti" />
  <AppBanner.Text>Baixe o App e ganhe descontos exclusivos</AppBanner.Text>
  <AppBanner.Link href="https://..." aria-label="Ver a página atual no aplicativo nativo.">Ver no app</AppBanner.Link>
</AppBanner>

const AppBannerText = styled.p`
  flex: 1;
  padding: 0 8px;
`

AppBannerText.propTypes = {
  children: PropTypes.node.isRequired
}

AppBanner.Text = AppBannerText


Transform this


import React, { useState, useEffect } from "react";

class Media extends React.Component {
  state = {
    matches: window.matchMedia(this.props.query)
      .matches
  };

  componentDidMount() {
    this.setup();
  }

  setup() {
    let media = window.matchMedia(this.props.query);
    let listener = () =>
      this.setState({ matches: media.matches });
    media.addListener(listener);
    this.removeListener = () =>
      media.removeListener(listener);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.query !== this.props.query) {
      this.removeListener();
      this.setState({
        matches: window.matchMedia(this.props.query)
          .matches
      });
      this.setup();
    }
  }

  componentWillUnmount() {
    this.removeListener();
  }

  render() {
    return this.props.children(this.state.matches);
  }
}

function App() {
  return (
    <Media query="(max-width: 400px)">
      {small => (
        <Media query="(min-width: 800px)">
          {large => (
            <div className="Media">
              <h1>Media</h1>
              <p>
                Small? {small ? "Yep" : "Nope"}.
              </p>
              <p>
                Large? {large ? "Yep" : "Nope"}.
              </p>
            </div>
          )}
        </Media>
      )}
    </Media>
  );
}

export default App;

into


import React, { useState, useEffect } from "react";

function useMedia(query) {
  let [matches, setMatches] = useState(
    window.matchMedia(query).matches
  );

  // cDM, cDU
  useEffect(
    () => {
      let media = window.matchMedia(query);
      if (media.matches !== matches) {
        setMatches(media.matches);
      }
      let listener = () =>
        setMatches(media.matches);
      media.addListener(listener);
      return () => media.removeListener(listener);
    },
    [query]
  );

  return matches;
}

function App() {
  let small = useMedia("(max-width: 400px)");
  let large = useMedia("(min-width: 800px)");
  return (
    <div className="Media">
      <h1>Media</h1>
      <p>Small? {small ? "Yep" : "Nope"}.</p>
      <p>Large? {large ? "Yep" : "Nope"}.</p>
    </div>
  );
}

export default App;

Ou melhor, extraia o custom hook


import { useState, useEffect } from "react";

let useMedia = query => {
  let [matches, setMatches] = useState(window.matchMedia(query));

  useEffect(
    () => {
      let media = window.matchMedia(query);
      let listener = () => setMatches(media.matches);
      media.addListener(listener);
      listener();
      return () => media.removeListener(listener);
    },
    [query]
  );

  return matches;
};

export default useMedia;

Clean Code


Funções pequenas

Responsabilidade única


Ninguém quer ler arquivos com 50 mil coisas


Escreva funções pequenas


Componentes são funções


function Component ({ children, ...props }) {
  return React.createElement(OtroComponente, props, children)
}

GitHub

Social Media


Ninguém coda mais sozinho


Times? Produto compartilhado? Pergunte pro amiguinho ao lado! Pergunte no Slack!


É isso

Qualquer dúvida, estamos aí.

Testando código


Bad bad server...


Divisão


Por tipo de desenvolvimento


TDD - Test-Driven Development


Escrever teste antes de codar


TDD Cycle


BDD - Behavior Driven Development


#language: pt-br
Funcionalidade: Vender doces
     Para quando uma doce for vendido
     Eu, como vendedor
     Desejo decrementar um item no estoque

Cenário: Baixa 1 bala do estoque
     Dado que cliente pede 1 bala
     E tenho 10 balas em estoque
     Quando ele compra realiza a compra
     Então eu fico com 9 balas em estoque

ATDD - Acceptance Test Driven Development


Mistura entre criar Stories (BDD) e aplicá-la durante desenvolvimento (TDD).


Por tipo de testes


História


Mainframes


Debug

Terminal


Punched Cards


Teste de mesa

Desk Check pun


Desk Check


Automatização

Assertion

Spongebob


a === b

cqd.


xUnit

xUnit


E no JavaScript?


Debug

console.log('minha função passou por aqui!')

console.log('sai da frente que debugando:', coisa)

debugger;

Assertion

console.log(minhaFuncao() === true)

Assertion mais inteligente

const assertTrue = fn => fn() === true

assertTrue(minhaFuncao)

xUnit

function assertTrueButThrowExceptionOnError (fn) {
  if (!fn()) {
    throw new Error('falhou aqui!')
  }
}

try {
  assertTrue(() => minhaFuncao())
  console.log('meus testes passaram!')
} catch (err) {
  console.log('meus testes falharam... :(')
  console.error(err)
}

Nos dias atuais


Agile Tests Quadrant


Testes Automatizados de Software


Testes Funcionais

Testar funcionalidades


Testes unitários

conveyor


Testes de integração

assemble


Testes end-to-end

cypress


Testes de Regressão

regression


Não irei abordar


Como testar?


Estilos de testes

exemplos da documentação do Chai


Assert

var assert = require('chai').assert
  , foo = 'bar'
  , beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

assert.typeOf(foo, 'string'); // without optional message
assert.typeOf(foo, 'string', 'foo is a string'); // with optional message
assert.equal(foo, 'bar', 'foo equal `bar`');
assert.lengthOf(foo, 3, 'foo`s value has a length of 3');
assert.lengthOf(beverages.tea, 3, 'beverages has 3 types of tea');

Expect

var answer = 43;

// AssertionError: expected 43 to equal 42.
expect(answer).to.equal(42);

// AssertionError: topic [answer]: expected 43 to equal 42.
expect(answer, 'topic [answer]').to.equal(42);

Should

var should = require('chai').should() //actually call the function
  , foo = 'bar'
  , beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

foo.should.be.a('string');
foo.should.equal('bar');
foo.should.have.lengthOf(3);
beverages.should.have.property('tea').with.lengthOf(3);

Estrutura


Asserção

// cenário do teste
test('when clicked, the counter increments the click', () => {
  fireEvent.click(counterButton)
  assert.equal(counterButton.innerHTML, '4') // Chai
})

Não existe assert no Jest (Jasmine)


BDD

describe('Counter Button', () => {
  describe('when clicked', () => {
    it('should increments the click', () => {
      fireEvent.click(counterButton)
      expect(counterButton).toHaveTextContent('4')
    })
  })
})

[PASS] src/Counter.spec.js
Counter Button
  when clicked
    ✓ should increments the click

o it substitui lexicamente o que foi citado anteriormente nos blocos describe


Boas Práticas


Test Suite


describe()

Próprio arquivo de teste


Isolamento dos testes


Cada teste deve ser independente


Ciclo de vida do teste

lifecycle


SetUp

Preparação prévia pro teste.


No Jest:

beforeAll()
beforeEach()

MythBusters


TearDown

"Limpeza" para o próximo teste.


No Jest:

afterEach()

Documentação para o Jest

https://jestjs.io/docs/en/setup-teardown


beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));
test('', () => console.log('1 - test'));
describe('Scoped / Nested block', () => {
  beforeAll(() => console.log('2 - beforeAll'));
  afterAll(() => console.log('2 - afterAll'));
  beforeEach(() => console.log('2 - beforeEach'));
  afterEach(() => console.log('2 - afterEach'));
  test('', () => console.log('2 - test'));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll

Ter pouca asserção por teste


Ser simples


describe('Meu teste', () => {
  it('deveria funcionar', () => {
    expect(button).toMatchSnapshot()
    expect(button).toBeTruthy()
    expect(button).toHaveAttribute('data-foo', 'bar')
    expect(button).toBeDisabled()
    expect(button).toHaveTextContent('Perdi!')
  })
})

E aí? Qual deles quebrou? Se esse teste quebrar, o que vai significar para mim? Onde está o erro?


Testes Unitários (?)


Write tests. Not too many. Mostly integration.

https://twitter.com/rauchg/status/807626710350839808

https://kentcdodds.com/blog/write-tests


https://twitter.com/kentcdodds/status/960723172591992832


tests


https://www.facebook.com/notes/kent-beck/unit-tests/1726369154062608/


Testes são abstrações


Teste funcionalidades, não implementação:

  • Implementação muda
  • Como refatorar se o teste está acoplado com a funcionalidade?
  • Teste também é código...
  • Alta Coesão, Baixo Acoplamento

ou melhor:

Teste comportamentos


BDD


E por que testes funcionais?


É mais barato fazer e dá para validar no Pipeline antes de subir.


React Testing Library


É praticamente um teste e2e


E por que não de integração?


O teste pode falhar bem na hora daquele PR e nenhum código pode subir enquanto tiver essa dependência


E o COVERAGE?


Mostra branches por onde seu código não passou


Será que minha implementação não está fazendo mais coisa que deveria?


Extras


Por que testo Snapshot?


Output


No caso do HTML: Semântica do código gerado


Matchers úteis do Jest


Jest

  • .not.
  • .arrayContaining
  • .objectContaining
  • .stringContaining

  • .toBeTruthy
  • .toBeFalsy
  • .toBeDefined
  • .toBeInstanceOf

  • .toHaveBeenCalled
    • .toHaveBeenCalledTimes
    • .toHaveBeenCalledWith
    • .toHaveBeenLastCalledWith
    • .toHaveBeenNthCalledWith

  • .resolves
  • .rejects
  • .toThrow

jest-dom

HTML - DOM

  • toBeDisabled
  • toBeEnabled
  • toBeEmpty
  • toBeInTheDocument
  • toBeInvalid
  • toBeRequired
  • toBeValid
  • toBeVisible
  • toContainElement
  • toContainHTML
  • toHaveAttribute
  • toHaveClass
  • toHaveFocus
  • toHaveFormValues
  • toHaveStyle
  • toHaveTextContent
  • toHaveValue
  • toBeChecked

jest-extended

https://github.com/jest-community/jest-extended


like a boss


Referência


Kent C. Dodds

https://kentcdodds.com/

Yarn 2 / Yarn Berry

Falarei um pouco sobre a nova versão do Yarn e algumas funcionalidades que me impactaram ao utilizá-lo.

No final do artigo deixarei também diversos links com outras tecnologias.

Breve história

Em 2016 o blog de engenharia do Facebook publicou o seguinte artigo:

Yarn: A new package manager for JavaScript

Apesar do npm vir junto desde as primeiras versões do Node.js, os desenvolvedores do Facebook estavam enfrentando alguns problemas em relação ao gerenciamento de pacotes e resolveram criar um outro gerenciador de pacotes.

Apesar de ambos utilizarem o mesmo registro público (o npmjs.com), mesmo nas versões iniciais o Yarn já tentava adicionar alguns diferenciais:

Lockfile

Um arquivo de lock (yarn.lock) de versões que pode ser versionável via gerenciador de controlador de versão (ex: Git). As dependências são registradas numa ordem determinística facilitando na hora do versionamento. Até então o npm ainda não tinha a implementação que gerava o arquivo package-lock.json e para travar versões de dependências tinha somente o npm-shrinkwrap. O Yarn também atualiza o yarn.lock quando há mudanças de versão das dependências quando atualizadas e satisfizerem as versões definidas no package.json.

Caching de pacotes

Outro diferencial é que o Yarn possui um registro próprio de cache de pacotes, onde dependendo do pacote e de sua versão o Yarn baixava um arquivo gzip contendo todos os arquivos de uma determinada dependência e só baixava do registro principal se este cache não existisse.

Instalação faseada

Antigamente o npm baixava tudo do registro e ia baixando e resolvendo dependências de acordo com o que estava definido no package.json de cada pacote. Ao baixar também criava pastas node_module dentro de cada dependência e jogando os pacotes recursivamente. Posteriormente o npm começou a fazer dedupe das dependências (tirando duplicações ao mesmo pacote ser dependência de múltiplos outros pacotes). Mesmo assim poderiam haver inconsistências na hora da instalação pois a estrutura do node_modules era feita de uma maneira não determinística.

Assim o Yarn ao invés de simplesmente instalar as dependências do package.json ele primeiro cria um grafo de todas as dependências, resolve e só então baixa as dependências depois da resolução deste grafo de dependências.

Yarn adding

Berry

Ambos os gerenciadores de pacotes foram acumulando novas funcionalidades... Vou falar de evoluções e algumas funcionalidades que já utilizei. Não comentarei sobre tudo que há de novo pois você mesmo pode acompanhar neste artigo que será uma comunicação muito mais oficial que meus comentários.

Plug'n'Play

Creio que a mais famosa e discutida das funcionalidades do Yarn é o pnp.

Se você ainda não ouviu falar, o Plug'n'Play é uma funcionalidade do Yarn que "some" com a node_modules do teu projeto. Foi introduzido na primeira versão do Yarn em setembro de 2018 e seu funcionamento foi inspirado no autoload do PHP (PSR-4).

Tecnicamente o Yarn cria um arquivo na raiz do seu projeto chamado .pnp.js gerado automagicamente e instala todas as dependências do teu projeto num diretório de cache (por padrão: ./.yarn/cache dentro do projeto), sendo uma alternativa ao antigo Yarn Offline Mirror. Ao rodar o node, basta dar require do arquivo .pnp.js e seu projeto está pronto para pegar as dependências resolvendo direto da pasta de cache e não mais da node_modules. E também ao instalar (rodando yarn install ou yarn add), se for um pacote que já existe no projeto o Yarn aproveita a referẽncia em disco antes de baixar o pacote do registro remoto.

E todas as dependências são agora guardadas dentro de arquivos zipados.

Ressalto que essa é uma funcionalidade exclusiva do Yarn em relação a sua funcionalidade. O npm tink também pretende adicionar Plug'n'Play mas sua implementação e funcionalidade podem divergir da feita pelo time do Yarn.

Hoje também existem diversas alternativas em relação a utilização de dependências.

Inclusive a especificação de ESM permite importar pacotes remotos via HTTP. Não é algo que está implementado no Node ainda porém o Deno já possui essa implementação. Também está implementado em navegadores mais atuais. Existe até um repositório/CDN de pacotes ESM para utilização tanto no navegador quanto no Deno.

Tenho um executável que não roda, e agora?

Existe um executável @yarnpkg/pnpify que auxilia ao rodar algum executável que não seja compatível com PnP emulando uma node_modules.

Monorepo

Outra funcionalidade já existente na primeira versão do gerenciador de pacotes é o Workspaces. Além de transformar o Yarn numa ferramenta para manipulação de monorepos, ao ativá-lo o Yarn também une e deduplica todas as dependências num só node_modules.

Digo, esta era o comportamento esperado na versão um. A diferença com o Berry é que como o PnP, essa funcionalidade também vem ativa por padrão. E ainda mais: sem node_modules. Ainda com os ganhos de linkagem automática de pacotes no mesmo repositório.

Também na nova versão não é mais necessário adicionar a flag --ignore-workspace-root-check/-W ao instalar dependências do pacote raiz. Ainda é possível utilizar o comando yarn workspace <nome do pacote> <ação> porém a diretiva yarn workspaces ... deixou de existir porém parte de sua funcionalidade virou yarn workspaces foreach .... Aproveitando...

Plugins

Com esta nova versão, funcionalidades específicas viraram plugins. Eles serão instalados dentro do projeto, podendo ou não ser versionados dentro da pasta .yarn/plugins. Listarei alguns:

Adiciona o comando yarn workspaces foreach ... que possibilita executar scripts em todos os pacotes do monorepo, inclusive em paralelo.

Adiciona o comando yarn upgrade-interactive, algo que já existia na versão anterior e foi melhorada nesta. Consiste em uma ferramenta (CLI) que possibilita atualizar pacote a pacote de cada dependência do projeto.

Tem um plugin bem legal para quem desenvolve em TypeScript. Ao utilizar este plugin ele verifica se uma dependência, ao instalar, possui um pacote no DefinitelyTyped (@types/*), baixa e adiciona nos devDependencies.

Configuração

Ao instalar plugins o Yarn gerará um arquivo de configuração, localizado na raiz com o nome de .yarnrc.yml. Nesta nova versão também foi convencionado o nome do arquivo como o anteriormente citado, evitando ambiguidades. Na versão anterior era possível ter um arquivo .yarnrc contendo todos os yarn set config ... do projeto.

Validação de dependências

Ao utilizar uma dependência o Yarn valida se essa dependência foi listada como dependência (incluindo devDependencies e peerDependencies) e se foi corretamente instalada.

Monkey-patch

Se alguma dependência não foi listada corretamente, quando a dependência for executada o Yarn emitirá um erro impedindo sua execução.

Mas em alguns casos não é possível esperar que atualizem a dependência. Para estes casos é possível adicionar packageExtensions dentro do .yarnrc.yml.

yarn dlx

Funcionalidade diliça que também não pode ficar de fora é o yarn dlx. Possui uma funcionalidade semelhante ao npx.

Na versão legacy o Yarn até possuia alguma alternativa ao npx com o yarn create, mas que acabava restringindo a execução apenas de pacotes que começassem com create-*.

E como instalar?

Por enquanto que ainda não houve atualização no npmjs da versão para a latest, instale como uma dependência global:

$ npm install -g yarn@berry

E como usar o antigo?

A versão atual se tornará a legacy, logo basta rodar:

$ npm install -g yarn@legacy

Observação: Se você utiliza nvm, por padrão o nvm usa diferentes pastas globais para cada versão do Node.

E como manter instalada a versão 2 mas usar a versão 1 em um projeto em específico?

Na pasta do projeto você pode rodar a seguinte instrução:

$ yarn set version ^1

Ele instalará o Yarn dentro da pasta .yarn/releases permitindo versionar junto do projeto e adicionando a chamada no arquivo de configuração .yarnrc.yml. Note que também é possível instalar uma versão específica mas que no exemplo foi sinalizado para instalar a última versão seguindo o semver.

Basta adicionar a dependência @yarnpkg/pnpify (como devDependencies) e rode uma instrução para gerar o SDK:

$ yarn add -D @yarnpkg/pnpify
$ yarn pnpify --sdk

Fazendo isso ele adicionará uma configuração no arquivo .vscode/settings.json seguido de uma dependência dentro da mesma pasta. Após isso, na próxima vez que vc abrir um arquivo TypeScript clique no canto direito abaixo onde mostra a versão do TypeScript e escolha a versão com o sufixo -pnpify e gg.

Documentação oficial

Quer saber mais, acesse a documentação oficial:

https://next.yarnpkg.com/

Outras tecnologias

Algumas para deixar no radar. Outras somente pra citar.

Antes de tudo não confundir Plug'n'Play com pnpm (com a mesma citação na documentação do Yarn). Este é um gerenciador de pacotes (também) mas que resolve a node_modules de um jeito flat, ou seja, mais horizontal possível e referenciando dependências usando symlinks (links simbólicos).

Gerenciador de monorepos, o mais popular dentre o desenvolvimento JS. Permite o uso tanto do Yarn quanto do npm e possui integração com Yarn Workspaces.

Analogamente ao Berry, o tink é a próxima major release (não em questão de versão e sim de breaking changes) para o npm.

Se quiser saber mais veja esse vídeo em inglẽs da JSConf'19.

Deno é um runtime para JavaScript (e TypeScript) feito pelo criador do Node.js Ryan Dahl, porém com a promessa de ser mais seguro.

Um registro de dependências e CDN de ESM na nuvem contendo pacotes que podem ser utilizados diretamente código rodado pelo navegador (e no Deno).

Um empacotador para navegadores atuais que possuem suporte a ESM. Transforma todas as dependências em módulos ESM para serem chamadas direto pelo código.

Resources

https://www.youtube.com/watch?v=bPae4Z8BFt8 https://twitter.com/kentcdodds/status/1220818615970099200 https://twitter.com/paularmstrong/status/1221146825958273025 https://twitter.com/dan_abramov/status/1221093758621683712

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment