Configure un proyecto TypeScript con dependencias comunes para construir múltiples archivos de salida JavaScript simples

10

Actualmente estoy escribiendo algunos guiones para Bot Land . Bot Land es un juego de estrategia en tiempo real en el que, en lugar de controlar tus unidades con un mouse y un teclado, escribes un código para controlar tus bots a través de una API, y luego tus bots van a luchar contra los bots de otros. Si está familiarizado con las unidades en SC2, puede crear bots que sean similares a los acechadores de parpadeo, tanques de asedio, médicos y ultraliscos. (Es un juego bastante divertido para los ingenieros de software, pero eso está fuera del alcance de esta pregunta).

tierra de bot

El control de bot tiene tres niveles de complejidad creciente: una IA predeterminada, un lenguaje de programación similar a Scratch y un conjunto reducido de JavaScript llamado BotLandScript. Aunque el editor incorporado para BotLandScript es razonable, debe cargar todo su código como un solo archivo con funciones globales de nivel superior en todas partes. Naturalmente, esto comienza a ser doloroso después de un tiempo si su código comienza a alargarse y los diferentes robots comparten las mismas funciones.

entorno de programación

Para facilitar la escritura de código para múltiples bots, reducir la posibilidad de errores involuntarios al codificar en JS desnudo y aumentar mis posibilidades de vencer a otros jugadores, configuré el proyecto TypeScript anterior para proporcionar una biblioteca común y un código para cada uno de mis bots . La estructura del directorio actual se parece aproximadamente a la siguiente:

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

libes el código común que se comparte entre los bots y proporciona definiciones de TypeScript para la API de Bot Land (no TS). Cada bot obtiene su propia carpeta, con un archivo que contiene el código del bot y el otro una placa repetitiva tsconfig.json:

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

Cuando tsconfig.jsonse construye cada uno , crea un correspondiente bot.jsque contiene el código transpilado del propio bot, así como todo el código que contiene common.js. Esta configuración es subóptima por varias razones, entre otras: requiere una gran cantidad de repeticiones, dificulta agregar nuevos bots, incluye muchos códigos innecesarios para cada bot y requiere que cada bot se construya por separado.

Sin embargo, según mi investigación hasta ahora , no parece que haya una manera fácil de hacer lo que quiero. En particular, el uso de la nueva tsc -bopción y referencias no funciona, porque eso requiere que el código se modularice y Bot Land requiere un solo archivo con todas las funciones definidas en el nivel superior.

¿Cuál es la mejor manera de lograr la mayor cantidad posible de lo siguiente?

  • No se requiere una nueva plantilla para agregar un nuevo bot (por ejemplo, no tsconfig.jsonpor bot)
  • Úselo importpara funciones comunes para evitar generar código no utilizado, pero luego ...
  • Todavía se muestran todas las funciones como un solo archivo en el formato específico de Bot Land
  • Un solo paso de compilación que produce múltiples archivos de salida, uno para cada bot
  • Bonificación: integración del proceso de compilación con VS Code. Actualmente hay una plantilla correspondiente tasks.jsonpara construir cada subproyecto.

Supongo vagamente que la respuesta probablemente también incluye algo como Grunt tsc, pero no sé lo suficiente para estar seguro.

Andrew Mao
fuente
¿Es necesario que todos los bots tengan carpetas separadas? ¿O es suficiente que cada bot esté en el nivel raíz en un solo archivo? (por ejemplo <root>/MissileKite.ts)
a1300
1
¿Se deben nombrar todos los archivos bot transpilados bot.js?
a1300
La raíz en un solo archivo sería preferible; están en carpetas separadas debido a la separación tsconfig.json. Los archivos bot transpilados se pueden nombrar de cualquier forma, preferiblemente la versión .js del archivo original. Lo configuré de esta manera ahora en el repositorio que se envía a build/MissileKite.js.
Andrew Mao
1
@ andrew-mao Puede echar un vistazo a mi plantilla para proyectos de GAS que aborda la mayoría de sus requisitos (pero apuntando a un entorno diferente) Si le conviene, podría adaptarlo en algún momento de la próxima semana. github.com/PopGoesTheWza/ts-gas-project-starter
PopGoesTheWza
¿Es tsconfig-gas.jsonlo relevante mirar allí?
Andrew Mao

Respuestas:

2

Aquí está mi intento de responder a sus requisitos.

Archivos notables:

  • src/tsconfig-botland.jsonmantiene la configuración de cualquier script bot.land (incluidas sus declaraciones personalizadas a las que me mudé types/bot-land/index.d.ts). Puede cambiar la strictconfiguración que utilicé.
  • src/tsconfig.jsoncontiene referencias a todos tus bots. Este es el archivo para editar cada vez que desee agregar otro script de bot

Un script de bot tiene al menos dos archivos: uno minimalista tsconfig.jsony uno o más .tsarchivos de script.

Por ejemplo src/AggroMiner/tsconfig.json:

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

En la mayoría de los casos, para iniciar una nueva secuencia de comandos de bot debe:

  1. copie cualquier carpeta bot (es decir src/AggroMiner) a una nueva carpeta ensrc
  2. edite el src/<newBotFolder>/tsconfig.jsonpara editar el outFilecon el nombre de su bot
  3. editar src/tsconfig.jsony agregar una referencia asrc/<newBotFolder>

Se ha establecido el siguiente npm/ yarnscript:

  • build para construir todos los bots
  • build-cleanque borra la buildcarpeta antes de ejecutar unbuild
  • formatejecutar Prettier en todos los .tsarchivos bajosrc
  • lint ejecutar una comprobación tslint en todos los scripts de bot

Ahora agotando sus requisitos:

  • No se requiere una nueva plantilla para agregar un nuevo bot (por ejemplo, no tsconfig.json por bot)

Para lograr esto, sería necesario crear algún script que enumerara la carpeta / scripts de su bots ... y configurara el correspondiente por bot tsconfig.jsony se ejecute tsc. A menos que sea estrictamente necesario, una configuración mínima (descrita anteriormente) podría ser suficiente.

  • Utilice la importación para funciones comunes para evitar generar código no utilizado, pero luego ...

Primero, tenga en cuenta que si comienza a usar algún módulo export/ importdeclaración, necesitará un tercero adicional para empacar / sacudir árboles para lograr una salida de archivo único. Por lo que pude deducir de Bot.land, sus scripts se están ejecutando en el servidor. A menos que el código muerto tenga un impacto en el rendimiento de su bot, realmente no me molestaría.

  • Todavía se muestran todas las funciones como un solo archivo en el formato específico de Bot Land

Hecho.

  • Un solo paso de compilación que produce múltiples archivos de salida, uno para cada bot

Hecho.

  • Bonificación: integración del proceso de compilación con VS Code. Actualmente hay un correspondiente boilerplate task.json para construir cada subproyecto.

Los npmscripts deberían aparecer en la lista de tareas de vsc (al menos lo hacen en la mía), lo que hace tasks.jsoninnecesario.

PopGoesTheWza
fuente
Deadcode es un buen compromiso para todo lo demás que hiciste aquí; ¿puede decirme por qué utilizó types/bot-landpara las definiciones y por qué eligió la strictconfiguración?
Andrew Mao
Tipos / bot-land / index.d.ts realmente es su .d.ts original de lib, renombrado y colocado de manera diferente. Supongo que describe el contexto general de ejecución de bot.land para todos los scripts y, por lo tanto, me aseguro de que siempre esté disponible en cada script de bot. La configuración 'estricta' solo está aquí porque copié perezosamente mis configuraciones favoritas (lo mismo para las configuraciones más bonitas). Esos deben adaptarse a la preferencia del usuario (usted).
PopGoesTheWza
Me pregunto si hay una razón habitual para poner eso typeso si esa era solo una forma específica de organizar lo que elegiste.
Andrew Mao
La única razón es que suponiendo que fuera el contexto bot.land. Piense en ello como tener los tipos @ / tipings de nodo ya disponibles en sus scripts de nodejs
PopGoesTheWza
1
La carpeta A / types es uno de los lugares convencionales donde se colocan declaraciones de tipos externos (es decir, un contexto de ejecución específico como el motor botland o módulos / paquetes JavaScript sin tipo, que no se usan aquí)
PopGoesTheWza
3

En realidad, podría usar referencias de proyectos. Siga estos pasos para obtener los mismos resultados que estaba obteniendo para sus archivos originales, con todas las funciones en el nivel superior en un solo archivo. Sin embargo, no pude encontrar una solución para importar solo las funciones necesarias en los bots. Es decir, sin utilizar importaciones y exportaciones.

En su tsconfig.json en la raíz

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

Luego, en su carpeta lib, agregue un tsconfig.json así

{
  "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"
  ]
}

Necesitamos hacer algunos ajustes en data.ts, movement.ts y utils.ts para que ts no nos moleste con errores de compilación.

data.ts

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

(...)

movimiento.ts


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

utils.ts

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

A continuación, agregamos base.json en la raíz (el tsconfig.json de los bots lo extenderá).

base.json

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

y tsconfig.json de los bots (adaptarse según los bots)

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

Eso es. Ahora solo corre

tsc -b
jperl
fuente
Así que pensé en algo como esto, pero la razón por la que no funciona es porque el archivo que se genera en tu rama tiene un montón de cosas como esta en la parte superior, y el juego necesita un archivo con todas las funciones. Entonces, tendría que improvisar manualmente todas las salidas compiladas para crear el archivo que subiría, en lugar de simplemente copiar y pegar el archivo. `" uso estricto "; exportaciones .__ esModule = true; var data_1 = require ("../ lib / data"); var movement_1 = require ("../ lib / movement"); var utils_1 = require ("../ lib / utils"); `
Andrew Mao
Pero funciona ya que lib también se genera (compila) en la carpeta de compilación (gracias a las referencias).
jperl
Estaba en el proceso de editar mi comentario, ver arriba. O eche un vistazo al resultado build/MissileKite.jsque se genera cuando crea el repositorio original.
Andrew Mao
@AndrewMao lo siento, solo que ahora entiendo lo que quisiste decir con "porque eso requiere que el código esté modularizado y Bot Land requiere un solo archivo con todas las funciones definidas en el nivel superior". Pensé en usar "prepend: true" pero eso requiere usar outFile y ts no nos permitirá compilar los archivos en lib ya que algunos dependen de otros.
jperl
@ AndrewMao He agregado soporte para Webpack. Edité la publicación y empujé los cambios en el repositorio. Avísame si es mejor.
jperl