Configure um projeto TypeScript com dependências comuns para criar vários arquivos de saída JavaScript simples

10

Atualmente, estou escrevendo alguns scripts para o Bot Land . Bot Land é um jogo de estratégia em tempo real onde, em vez de controlar suas unidades com um mouse e teclado, você escreve código para controlar seus bots por meio de uma API e depois seus bots lutam contra os bots de outros. Se você conhece as unidades do SC2, pode criar bots semelhantes a perseguidores, tanques de cerco, médicos e ultraliscos. (É um jogo bastante divertido para engenheiros de software, mas está fora do escopo desta pergunta.)

terra bot

O controle de bot tem três níveis de complexidade crescente: uma IA padrão, uma linguagem de programação semelhante a Scratch e um conjunto reduzido de JavaScript chamado BotLandScript. Embora o editor interno do BotLandScript seja razoável, você precisa fazer o upload de todo o seu código como um único arquivo com funções globais de nível superior em qualquer lugar. Naturalmente, isso começa a ficar doloroso depois de um tempo, se o seu código começar a ficar longo e diferentes robôs compartilham as mesmas funções.

ambiente de programação

Para facilitar a escrita do código para vários bots, reduzir a chance de erros não intencionais ao codificar em JS básico e aumentar minhas chances de derrotar outros jogadores, configurei o projeto TypeScript acima para fornecer uma biblioteca comum e código para cada um dos meus bots . A estrutura de diretórios atual se parece aproximadamente com o seguinte:

lib/ 
  bot.land.d.ts
  common.ts
BlinkStalker/
  BlinkStalker.ts
  tsconfig.json
Artillery/
  Artillery.ts
  tsconfig.json
SmartMelee/
  SmartMelee.ts
  tsconfig.json

libé o código comum que é compartilhado entre os bots e fornece definições TypeScript para a API Bot Land (não TS). Cada bot recebe sua própria pasta, com um arquivo contendo o código do bot e o outro um clichê tsconfig.json:

{
  "compilerOptions": {
    "target": "es3",
    "module": "none",
    "sourceMap": false,
    "outFile": "bot.js"
  },
  "files": [
    "MissileKite.ts"
  ],
  "include": [
    "../lib/**/*"
  ]
}

Quando cada um tsconfig.jsoné criado, ele cria um correspondente bot.jsque contém o código transpilado do próprio bot, bem como todo o código common.js. Essa configuração é subótima por alguns motivos, entre outros: requer muita clichê duplicada, dificulta a adição de novos bots, inclui muitos códigos desnecessários para cada bot e exige que cada bot seja construído separadamente.

No entanto, com base em minha pesquisa até agora , não parece haver uma maneira fácil de fazer o que eu quero. Em particular, o uso da nova tsc -bopção e referências não funciona, porque isso exige que o código seja modularizado e o Bot Land exige um único arquivo com todas as funções definidas no nível superior.

Qual é a melhor maneira de obter o maior número possível de opções a seguir?

  • Não é necessário nenhum novo padrão para adicionar um novo bot (por exemplo, não tsconfig.jsonpor bot)
  • Use importpara funções comuns para evitar a saída de código não utilizado, mas depois ...
  • Ainda gera todas as funções como um único arquivo no formato específico do Bot Land
  • Uma única etapa de construção que produz vários arquivos de saída, um para cada bot
  • Bônus: integrando o processo de compilação com o VS Code. Atualmente, existe um modelo correspondente tasks.jsonpara a construção de cada subprojeto.

Eu suponho vagamente que a resposta provavelmente envolva algo como Grunt, além de tsc, mas eu não sei o suficiente sobre isso para ter certeza.

Andrew Mao
fonte
É necessário que todos os bots tenham pastas separadas? Ou é suficiente que cada bot esteja no nível raiz em um único arquivo? (por exemplo <root>/MissileKite.ts)
a1300 10/10/19
11
Todos os arquivos bot transpilados devem ser nomeados bot.js?
A1300
Raiz em um único arquivo seria preferível; eles estão em pastas separadas por causa do separado tsconfig.json. Os arquivos bot transpilados podem ter qualquer nome, de preferência a versão .js do arquivo original. Eu tenho que configurar desta maneira agora no repo saída para build/MissileKite.js.
Andrew Mao
11
@ andrew-mao Você pode dar uma olhada no meu modelo para projetos de GAS que atendem à maioria dos seus requisitos (mas direcionados a um ambiente diferente). github.com/PopGoesTheWza/ts-gas-project-starter
PopGoesTheWza
O que é tsconfig-gas.jsonrelevante para olhar lá?
Andrew Mao

Respostas:

2

Aqui está minha tentativa de responder às suas necessidades.

Arquivos notáveis:

  • src/tsconfig-botland.jsonmantém configurações para qualquer script bot.land (incluindo suas declarações personalizadas para as quais mudei types/bot-land/index.d.ts). Você pode alterar as strictconfigurações que eu usei.
  • src/tsconfig.jsonmantém referências a todos os seus bots. Este é o arquivo para editar sempre que você quiser adicionar outro script bot

Um script de bot tem pelo menos dois arquivos: um minimalista tsconfig.jsone um ou mais .tsarquivos de script.

Por exemplo src/AggroMiner/tsconfig.json:

{
    "extends": "../tsconfig-botland",
    "compilerOptions": {
        "outFile": "../../build/AggroMiner.js"
    },
    "files": ["index.ts"],
    "include": ["**/*.ts", "../lib/**/*.ts"]
}

Na maioria dos casos, para iniciar um novo script de bot, você deve:

  1. copie qualquer pasta bot (ou seja src/AggroMiner) para uma nova pasta emsrc
  2. edite o src/<newBotFolder>/tsconfig.jsonpara editar o outFilecom o nome do seu bot
  3. editar src/tsconfig.jsone adicionar uma referência asrc/<newBotFolder>

O seguinte npm/ yarnscript foi definido:

  • build construir todos os bots
  • build-cleanque limpa a buildpasta antes de executar umbuild
  • formatpara executar o Prettier em todos os .tsarquivos emsrc
  • lint executar uma verificação tslint em todos os scripts de bot

Agora, executando seus requisitos:

  • Não é necessário um novo padrão para adicionar um novo bot (por exemplo, não tsconfig.json por bot)

Para conseguir isso, seria necessário criar um script que enumere sua pasta / scripts de bots ... e configure o relevante por bot tsconfig.jsone execute tsc. A menos que seja estritamente necessário, uma configuração mínima (descrita acima) pode ser suficiente.

  • Use import para funções comuns para evitar a saída de código não utilizado, mas depois ...

Primeiro, esteja ciente de que, se você começar a usar qualquer módulo export/ importinstrução, precisará de terceiros adicionais para compactar / treeshake, a fim de obter uma única saída de arquivo. Pelo que pude reunir do Bot.land, seus scripts estão em execução no servidor. A menos que o código morto tenha um impacto no desempenho do seu bot, eu realmente não me incomodaria.

  • Ainda gera todas as funções como um único arquivo no formato específico do Bot Land

Feito.

  • Uma única etapa de construção que produz vários arquivos de saída, um para cada bot

Feito.

  • Bônus: integrando o processo de compilação com o VS Code. Atualmente, existe um clichê.json padrão para a construção de cada subprojeto.

Os npmscripts devem aparecer na lista de tarefas do vsc (pelo menos na minha), tornando o tasks.jsondesnecessário.

PopGoesTheWza
fonte
O código morto é um bom compromisso para tudo o que você fez aqui; você pode me informar por que você usou types/bot-landas definições e por que escolheu as strictconfigurações?
Andrew Mao
Types / bot-land / index.d.ts realmente é o seu .d.ts original da lib, renomeado e colocado de forma diferente. Suponha que ele descreva o contexto geral de execução bot.land para todos os scripts e, portanto, garanto que ele esteja sempre disponível em todos os scripts de bot. As configurações 'estritas' estão aqui apenas porque copiei preguiçosamente minhas configurações favoritas (o mesmo para as configurações mais bonitas). Esses devem ser adaptados à preferência do usuário (você).
PopGoesTheWza 15/10/19
Só estou me perguntando se existe uma razão costumeira para colocar isso typesou se foi apenas uma maneira específica de organizar a sua escolha.
Andrew Mao
A única razão é que assumindo que era o contexto bot.land. Pense nisso como ter os tipos @ / tipagens nó já está disponível em seus scripts NodeJS
PopGoesTheWza
11
A / tipos pasta é um dos lugares convencionais, onde um coloca tipos externos declarações (Ie contexto de execução específica, como o motor botland ou sem tipo JavaScript módulos / pacotes, que não são utilizados aqui)
PopGoesTheWza
3

Você poderia realmente usar referências de projeto. Siga estas etapas para obter os mesmos resultados dos arquivos originais, com todas as funções no nível superior em um arquivo. No entanto, não consegui encontrar uma solução para importar apenas as funções necessárias em bots. Ou seja, sem usar importações e exportações.

No seu tsconfig.json na raiz

{
    "files": [],
    "references": [
        { "path": "./lib" }
        { "path": "./AggroMiner" }
        { "path": "./ArtilleryMicro" }
        { "path": "./MissileKite" }
        { "path": "./SmartMelee" }
        { "path": "./ZapKite" }
    ]
}

Em seguida, na sua pasta lib, adicione um tsconfig.json assim

{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "composite": true,
    "rootDir": ".",
    "outFile": "../build/lib.js",
    "target": "es3",
    "removeComments": true,
    "sourceMap": false,
  },
  "files": [
    "data.ts",
    "movement.ts",
    "utils.ts"
  ]
}

Precisamos fazer alguns ajustes em data.ts, motion.ts e utils.ts para que ts não nos incomode com erros de compilação.

data.ts

/// <reference path="./bot.land.d.ts"/>

(...)

Movement.ts


/// <reference path="./data.ts"/>
/// <reference path="./utils.ts"/>
(...)

utils.ts

/// <reference path="./bot.land.d.ts"/>
(...)

Em seguida, adicionamos base.json na raiz (o tsconfig.json dos bots o estenderá).

base.json

{
  "compilerOptions": {
    "declaration": true,
    "composite": true,
    "rootDir": ".",
    "target": "es3",
    "removeComments": true,
    "sourceMap": false,
  }
}

e o tsconfig.json dos bots (adaptar de acordo com os bots)

{
  "extends": "../base",
  "compilerOptions": {
    "outFile": "../build/AggroMiner.js",
  },
  "files": [
    "AggroMiner.ts"
  ],
  "references": [
      { "path": "../lib", "prepend": true } //note the prepend: true
  ]
}

É isso aí. Agora apenas corra

tsc -b
jperl
fonte
Então pensei em algo assim, mas a razão pela qual não funciona é porque o arquivo que sai da sua ramificação tem um monte de coisas como essa no topo, e o jogo precisa de um arquivo com todas as funções. Então, eu teria que juntar manualmente todas as saídas compiladas para criar o arquivo que eu carregaria, em vez de apenas copiar e colar o arquivo. `" use estrito "; exporta .__ esModule = true; var data_1 = require ("../ lib / data"); var movimento_1 = requer ("../lib / movimento"); var utils_1 = require ("../ lib / utils"); `
Andrew Mao
Mas funciona, já que a lib também é produzida (criada) na pasta build (graças às referências).
jperl
Eu estava no processo de editar meu comentário - veja acima. Ou dê uma olhada no build/MissileKite.jsque é produzido quando você cria o repositório original.
Andrew Mao
@AndrewMao desculpe, só agora eu entendo o que você quis dizer com "porque isso exige que o código seja modularizado e o Bot Land exige um único arquivo com todas as funções definidas no nível superior". Pensei em usar "prepend: true", mas isso requer o uso de outFile e ts não nos permitirá compilar os arquivos na lib, pois alguns são dependentes de outros.
jperl
@AndrewMao Adicionei suporte ao Webpack. Editei a postagem e forcei as alterações no repositório. Deixe-me saber se é melhor.
jperl