DEV Community

Cover image for Começando um projeto Web com TypeScript
Guilherme Siquinelli
Guilherme Siquinelli

Posted on

7

Começando um projeto Web com TypeScript

Para começar um projeto do zero, simples, usando HTML, CSS, TypeScript e ESBuild.

Precisa fazer as seguintes etapas:

  1. Criar o diretório com o nome do seu projeto
  2. Criar o package.json
  3. Criar o tsconfig.json
  4. Instalar as dependências
    1. esbuild
    2. esbuild-serve
    3. typescript
    4. prettier
  5. Criar o diretório src
  6. Escrever o TypeScript inicial dentro de src
  7. Criar o diretório public/styles
  8. Escrever o HTML inicial dentro de public
  9. Escrever o CSS inicial dentro de public/styles
  10. Criar a configuração do ESBuild
  11. Criar a configuração do prettier
  12. Adicionar "type": "module" ao package.json
  13. Adicionar o script de build ao package.json
  14. Adicionar o script de start ao package.json
  15. Executar o npm start

Ou então compactar tudo isso em um script bash e usa-lo da seguinte forma

./create-project.sh meu-projeto
Enter fullscreen mode Exit fullscreen mode

Segue o código do script

#!/bin/bash
# Para usar, dê permissão de execução ao script
# usando o comando: chmod +x create-project.sh
# e então execute: ./create-project.sh projeto
mkdir $1
cd $1
npm init -y
echo '{
"compilerOptions": {
"target": "es5",
"lib": [
"es2020",
"dom",
"dom.iterable"
],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": false,
"skipLibCheck": true
}
}' > tsconfig.json
npm i -D esbuild esbuild-serve typescript@latest prettier
mkdir src
echo "document.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector('form')
if (form) {
const button = form.querySelector('button')
if (button) button.disabled = true
form.onchange = () => {
const valid = !form.checkValidity()
if (button) button.disabled = valid
}
form.onsubmit = (ev) => {
ev.preventDefault()
const data = new FormData(form)
const values = Object.fromEntries(data.entries())
console.log(values)
}
}
})
" > src/main.ts
mkdir -p public/styles
echo '<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="styles/main.css" />
<script src="main.js" defer></script>
<title>Página da Web</title>
</head>
<body>
<header>
<h1>'$1'</h1>
</header>
<main>
<article>
<h2>Artigo sobre JavaScript</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sin
laboramus, quis est, qui alienae modum statuat industriae? Traditur,
inquit, ab Epicuro ratio neglegendi doloris. Duo Reges: constructio
interrete. Te enim iudicem aequum puto, modo quae dicat ille bene
noris. Quamquam id quidem licebit iis existimare, qui legerint. Sed
quid sentiat, non videtis. Duo enim genera quae erant, fecit tria.
Quodsi ipsam honestatem undique pertectam atque absolutam. Odium autem
et invidiam facile vitabis. Consequens enim est et post oritur, ut
dixi.
</p>
<pre><code>document.addEventListener("DOMContentLoaded", () => {
const form = document.querySelector("form")
if (form) {
const button = form.querySelector("button")
if (button) button.disabled = true
form.onchange = () => {
const valid = !form.checkValidity()
if (button) button.disabled = valid
}
form.onsubmit = (ev) => {
ev.preventDefault()
const data = new FormData(form)
const values = Object.fromEntries(data.entries())
console.log(values)
}
}
})</code></pre>
</article>
<form>
<section>
<label for="name">Nome</label>
<input
id="name"
type="text"
autofocus
autocomplete="given-name"
name="name"
required
/>
<output>Obrigatório</output>
</section>
<section>
<label for="email">E-mail</label>
<input
id="email"
type="email"
name="email"
autocomplete="email"
required
/>
<output>Email inválido</output>
</section>
<section>
<label for="password">Senha</label>
<input
id="password"
type="password"
name="password"
autocomplete="new-password"
minlength="6"
required
/>
<output>Mínimo de 6 caracteres</output>
</section>
<section>
<label> <input type="checkbox" name="terms" /> Li e concordo com os termos </label>
</section>
<hr />
<button>Enviar</button>
</form>
</main>
<footer>&copy; 2021</footer>
</body>
</html>
' > public/index.html
echo ':root {
/* Palette generated by Material Palette
materialpalette.com/deep-purple/cyan */
--dark-primary-color: #512da8;
--default-primary-color: #673ab7;
--light-primary-color: #d1c4e9;
--text-primary-color: #ffffff;
--accent-color: #00bcd4;
--primary-text-color: #212121;
--secondary-text-color: #757575;
--divider-color: #bdbdbd;
--warn-color: #eb3535;
--default-font-family: "Fira Sans", sans-serif;
--default-element-margin: 6px;
--default-element-padding: 8px;
--default-block-padding: 10px;
--default-border-width: 2px;
--border-radius-xs: 2px;
--border-radius-sm: 4px;
--border-radius-md: 8px;
--border-radius-lg: 12px;
}
html,
body {
height: 100%;
}
body {
margin: 0;
display: flex;
flex-direction: column;
accent-color: var(--accent-color);
color: var(--primary-text-color);
font-family: var(--default-font-family);
}
main {
flex: 1;
padding: var(--default-block-padding);
}
header,
footer {
color: var(--text-primary-color);
padding: var(--default-block-padding);
background-color: var(--dark-primary-color);
}
button {
padding: var(--default-element-padding)
calc(var(--default-element-padding) * 2);
background-color: var(--default-primary-color);
border-radius: var(--border-radius-sm);
color: var(--text-primary-color);
border: 0;
}
button:disabled {
background-color: var(--light-primary-color);
}
button.accent {
background-color: var(--accent-color);
}
form section output {
visibility: hidden;
}
form.invalid input:invalid,
form section input:invalid:focus + output {
visibility: visible;
}
form section output {
color: var(--warn-color);
}
form section {
display: flex;
flex-direction: column;
margin-top: var(--default-element-margin);
margin-bottom: var(--default-element-margin);
}
input {
border-radius: var(--border-radius-sm);
padding: var(--default-element-padding);
border: var(--default-border-width) solid var(--dark-primary-color);
}
input[type="checkbox"] {
transform: scale(1.2);
}
pre {
padding: var(--default-block-padding);
background-color: var(--primary-text-color);
}
code {
font-size: 16px;
color: var(--light-primary-color);
}
' > public/styles/main.css
echo 'import esbuildServe from "esbuild-serve";
esbuildServe(
{
logLevel: "info",
entryPoints: ["src/main.ts"],
bundle: true,
outfile: "public/main.js",
sourcemap: true,
},
{ root: "public" }
);' > esbuild.config.js
echo '{
"semi": false,
"singleQuote": true
}' > .prettierrc
npx json -I -f package.json -e "this.type = 'module';"
npx json -I -f package.json -e "this.scripts.build = 'node esbuild.config.js';"
npx json -I -f package.json -e "this.scripts.start = 'node esbuild.config.js -w';"
npm start

Ele é extremamente simples e resolve o propósito com excelência.

Espero ter ajudado, até a próxima.
[]s

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️