Init files

This commit is contained in:
Anton Vakhrushev 2020-05-31 15:06:55 +03:00
commit 3cd6a07fbf
29 changed files with 947 additions and 0 deletions

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
[*.rb]
indent_style = space
indent_size = 4

1
.env Normal file
View File

@ -0,0 +1 @@
NODE_IMAGE=screeps-node

92
.gitignore vendored Normal file
View File

@ -0,0 +1,92 @@
# yarn lock file
/yarn.lock
# npm lock file (v5.0.0+)
/package-lock.json
# Ignore basic folders
/dist
/.rpt2_cache
/tsc-out
/node_modules
/_book
/build/*
# TypeScript definitions installed by Typings
/typings
# Screeps Config
screeps.json
# ScreepsServer data from integration tests
/server
# Numerous always-ignore extensions
*.diff
*.err
*.orig
*.log
*.rej
*.swo
*.swp
*.zip
*.vi
*~
# Editor folders
.cache
.project
.settings
.tmproj
*.esproj
nbproject
*.sublime-project
*.sublime-workspace
.idea
# =========================
# Operating System Files
# =========================
# OSX
# =========================
.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"semi": true,
"tabWidth": 2,
"printWidth": 120,
"singleQuote": true,
"trailingComma": "none"
}

6
Dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM node:10-alpine
RUN apk add --no-cache --virtual .gyp \
python \
make \
g++

16
Makefile Normal file
View File

@ -0,0 +1,16 @@
all: format build
build-docker:
docker build -t screeps-node .
build:
tools/npm run build:dev
format:
tools/npm run format
test:
tools/npm run test
coverage:
tools/npm run coverage

55
README.md Normal file
View File

@ -0,0 +1,55 @@
# Screeps Typescript Starter
Screeps Typescript Starter is a starting point for a Screeps AI written in Typescript. It provides everything you need to start writing your AI whilst leaving `main.ts` as empty as possible.
## Basic Usage
You will need:
- [Node.JS](https://nodejs.org/en/download) (10.x)
- A Package Manager ([Yarn](https://yarnpkg.com/en/docs/getting-started) or [npm](https://docs.npmjs.com/getting-started/installing-node))
- Rollup CLI (Optional, install via `npm install -g rollup`)
Download the latest source [here](https://github.com/screepers/screeps-typescript-starter/archive/master.zip) and extract it to a folder.
Open the folder in your terminal and run your package manager to install install the required packages and TypeScript declaration files:
```bash
# npm
npm install
# yarn
yarn
```
Fire up your preferred editor with typescript installed and you are good to go!
### Rollup and code upload
Screeps Typescript Starter uses rollup to compile your typescript and upload it to a screeps server.
Move or copy `screeps.sample.json` to `screeps.json` and edit it, changing the credentials and optionally adding or removing some of the destinations.
Running `rollup -c` will compile your code and do a "dry run", preparing the code for upload but not actually pushing it. Running `rollup -c --environment DEST:main` will compile your code, and then upload it to a screeps server using the `main` config from `screeps.json`.
You can use `-cw` instead of `-c` to automatically re-run when your source code changes - for example, `rollup -cw --environment DEST:main` will automatically upload your code to the `main` configuration every time your code is changed.
Finally, there are also NPM scripts that serve as aliases for these commands in `package.json` for IDE integration. Running `npm run push-main` is equivalent to `rollup -c --environment DEST:main`, and `npm run watch-sim` is equivalent to `rollup -cw --dest sim`.
#### Important! To upload code to a private server, you must have [screepsmod-auth](https://github.com/ScreepsMods/screepsmod-auth) installed and configured!
## Typings
The type definitions for Screeps come from [typed-screeps](https://github.com/screepers/typed-screeps). If you find a problem or have a suggestion, please open an issue there.
## Documentation
We've also spent some time reworking the documentation from the ground-up, which is now generated through [Gitbooks](https://www.gitbook.com/). Includes all the essentials to get you up and running with Screeps AI development in TypeScript, as well as various other tips and tricks to further improve your development workflow.
Maintaining the docs will also become a more community-focused effort, which means you too, can take part in improving the docs for this starter kit.
To visit the docs, [click here](https://screepers.gitbook.io/screeps-typescript-starter/).
## Contributing
Issues, Pull Requests, and contribution to the docs are welcome! See our [Contributing Guidelines](CONTRIBUTING.md) for more details.

66
package.json Normal file
View File

@ -0,0 +1,66 @@
{
"name": "screeps-typescript-starter",
"version": "3.0.0",
"description": "",
"main": "index.js",
"//": "If you add or change the names of destinations in screeps.json, make sure you update these scripts to reflect the changes",
"scripts": {
"lint": "tslint -p tsconfig.json \"src/**/*.ts\"",
"build": "rollup -c",
"push-main": "rollup -c --environment DEST:main",
"push-pserver": "rollup -c --environment DEST:pserver",
"push-sim": "rollup -c --environment DEST:sim",
"format": "prettier --write \"src/**/*.ts\"",
"test": "npm run test-unit",
"test-unit": "rollup -c rollup.test-unit-config.js && mocha dist/test-unit.bundle.js",
"test-integration": "echo 'See docs/in-depth/testing.md for instructions on enabling integration tests'",
"watch-main": "rollup -cw --environment DEST:main",
"watch-pserver": "rollup -cw --environment DEST:pserver",
"watch-sim": "rollup -cw --environment DEST:sim"
},
"repository": {
"type": "git",
"url": "git+https://github.com/screepers/screeps-typescript-starter.git"
},
"author": "",
"license": "Unlicense",
"bugs": {
"url": "https://github.com/screepers/screeps-typescript-starter/issues"
},
"homepage": "https://github.com/screepers/screeps-typescript-starter#readme",
"engines": {
"node": "10.x"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.1.0",
"@rollup/plugin-multi-entry": "^3.0.0",
"@rollup/plugin-node-resolve": "^7.1.3",
"@types/chai": "^4.1.6",
"@types/lodash": "3.10.2",
"@types/mocha": "^5.2.5",
"@types/node": "^13.13.1",
"@types/screeps": "^3.1.0",
"@types/sinon": "^5.0.5",
"@types/sinon-chai": "^3.2.0",
"chai": "^4.2.0",
"lodash": "^3.10.1",
"mocha": "^5.2.0",
"prettier": "^2.0.4",
"rollup": "^2.6.1",
"rollup-plugin-buble": "^0.19.8",
"rollup-plugin-clear": "^2.0.7",
"rollup-plugin-nodent": "^0.2.2",
"rollup-plugin-screeps": "^1.0.0",
"rollup-plugin-typescript2": "^0.27.0",
"sinon": "^6.3.5",
"sinon-chai": "^3.2.0",
"ts-node": "^8.8.2",
"tslint": "^6.1.1",
"tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.3.0",
"typescript": "^3.8.3"
},
"dependencies": {
"source-map": "~0.6.1"
}
}

32
rollup.config.js Normal file
View File

@ -0,0 +1,32 @@
"use strict";
import clear from 'rollup-plugin-clear';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import screeps from 'rollup-plugin-screeps';
let cfg;
const dest = process.env.DEST;
if (!dest) {
console.log("No destination specified - code will be compiled but not uploaded");
} else if ((cfg = require("./screeps.json")[dest]) == null) {
throw new Error("Invalid upload destination");
}
export default {
input: "src/main.ts",
output: {
file: "dist/main.js",
format: "cjs",
sourcemap: true
},
plugins: [
clear({ targets: ["dist"] }),
resolve(),
commonjs(),
typescript({tsconfig: "./tsconfig.json"}),
screeps({config: cfg, dryRun: cfg == null})
]
}

View File

@ -0,0 +1,34 @@
"use strict";
import clear from 'rollup-plugin-clear';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import buble from 'rollup-plugin-buble';
import multiEntry from '@rollup/plugin-multi-entry';
import nodent from 'rollup-plugin-nodent';
export default {
input: 'test/integration/**/*.test.ts',
output: {
file: 'dist/test-integration.bundle.js',
name: 'lib',
sourcemap: true,
format: 'iife',
globals: {
chai: 'chai',
it: 'it',
describe: 'describe'
}
},
external: ['chai', 'it', 'describe'],
plugins: [
clear({ targets: ["dist/test.bundle.js"] }),
resolve(),
commonjs(),
typescript({tsconfig: "./tsconfig.test-integration.json"}),
nodent(),
multiEntry(),
buble()
]
}

View File

@ -0,0 +1,32 @@
"use strict";
import clear from 'rollup-plugin-clear';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import buble from 'rollup-plugin-buble';
import multiEntry from '@rollup/plugin-multi-entry';
export default {
input: 'test/unit/**/*.test.ts',
output: {
file: 'dist/test-unit.bundle.js',
name: 'lib',
sourcemap: true,
format: 'iife',
globals: {
chai: 'chai',
it: 'it',
describe: 'describe'
}
},
external: ['chai', 'it', 'describe'],
plugins: [
clear({ targets: ["dist/test.bundle.js"] }),
resolve(),
commonjs(),
typescript({tsconfig: "./tsconfig.json"}),
multiEntry(),
buble()
]
}

27
screeps.sample.json Normal file
View File

@ -0,0 +1,27 @@
{
"main": {
"token": "YOUR_TOKEN",
"protocol": "https",
"hostname": "screeps.com",
"port": 443,
"path": "/",
"branch": "main"
},
"sim": {
"token": "YOUR_TOKEN",
"protocol": "https",
"hostname": "screeps.com",
"port": 443,
"path": "/",
"branch": "sim"
},
"pserver": {
"email": "username",
"password": "Password",
"protocol": "http",
"hostname": "1.2.3.4",
"port": 21025,
"path": "/",
"branch": "main"
}
}

93
src/harvester.ts Normal file
View File

@ -0,0 +1,93 @@
/** @param {Creep} creep **/
export function runAsHarvester(creep: Creep) {
if (creep.store.getFreeCapacity() > 0) {
let sources = creep.room.find(FIND_SOURCES);
if (creep.harvest(sources[0]) === ERR_NOT_IN_RANGE) {
creep.moveTo(sources[0], { visualizePathStyle: { stroke: '#ffaa00' } });
}
} else {
let targets = creep.room.find(FIND_STRUCTURES, {
filter: (structure) => {
return (
(structure.structureType === STRUCTURE_EXTENSION ||
structure.structureType === STRUCTURE_SPAWN ||
structure.structureType === STRUCTURE_TOWER) &&
structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0
);
}
});
if (targets.length > 0) {
if (creep.transfer(targets[0], RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) {
creep.moveTo(targets[0], { visualizePathStyle: { stroke: '#ffffff' } });
}
} else {
const spawns = creep.room.find(FIND_MY_SPAWNS);
if (spawns.length > 0) {
creep.say('to spawn');
creep.moveTo(spawns[0]);
}
}
}
}
/** @param {Creep} creep **/
export function runAsBuilder(creep: Creep) {
const memory = creep.memory as CreepMemory & { building: boolean | undefined };
if (memory.building && creep.store[RESOURCE_ENERGY] === 0) {
memory.building = false;
creep.say('🔄 harvest');
}
if (!memory.building && creep.store.getFreeCapacity() === 0) {
memory.building = true;
creep.say('🚧 build');
}
if (memory.building) {
const targets = creep.room.find(FIND_CONSTRUCTION_SITES);
if (targets.length > 0) {
if (creep.build(targets[0]) == ERR_NOT_IN_RANGE) {
creep.moveTo(targets[0], { visualizePathStyle: { stroke: '#ffffff' } });
}
} else {
const spawns = creep.room.find(FIND_MY_SPAWNS);
if (spawns.length > 0) {
creep.say('to spawn');
creep.moveTo(spawns[0]);
}
}
} else {
const sources = creep.room.find(FIND_SOURCES);
if (creep.harvest(sources[0]) == ERR_NOT_IN_RANGE) {
creep.moveTo(sources[0], { visualizePathStyle: { stroke: '#ffaa00' } });
}
}
}
/** @param {Creep} creep **/
export function runAsUpgrader(creep: Creep) {
const memory = creep.memory as CreepMemory & { upgrading: boolean | undefined };
if (memory.upgrading && creep.store[RESOURCE_ENERGY] === 0) {
memory.upgrading = false;
creep.say('🔄 harvest');
}
if (!memory.upgrading && creep.store.getFreeCapacity() === 0) {
memory.upgrading = true;
creep.say('⚡ upgrade');
}
if (memory.upgrading) {
if (creep.room.controller && creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
creep.moveTo(creep.room.controller, { visualizePathStyle: { stroke: '#ffffff' } });
}
} else {
const sources = creep.room.find(FIND_SOURCES);
if (creep.harvest(sources[0]) == ERR_NOT_IN_RANGE) {
creep.moveTo(sources[0], { visualizePathStyle: { stroke: '#ffaa00' } });
}
}
}

77
src/main.ts Normal file
View File

@ -0,0 +1,77 @@
import { ErrorMapper } from 'utils/ErrorMapper';
import { runAsBuilder, runAsHarvester, runAsUpgrader } from './harvester';
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz1234567890';
const ALPHABET_LENGTH = ALPHABET.length - 1;
function generateId(count: number): string {
let str = '';
for (let i = 0; i < count; ++i) {
let symbolIndex = Math.floor(Math.random() * ALPHABET_LENGTH);
str += ALPHABET[symbolIndex];
}
return str;
}
export function uniqId(prefix: string = 'id'): string {
return prefix + generateId(16);
}
const ROLE_HARVESTER = 'harvester';
const ROLE_UPGRADER = 'upgrader';
const ROLE_BUILDER = 'builder';
// When compiling TS to JS and bundling with rollup, the line numbers and file names in error messages change
// This utility uses source maps to get the line numbers and file names of the original, TS source code
export const loop = ErrorMapper.wrapLoop(() => {
console.log(`Current game tick is ${Game.time}`);
// Automatically delete memory of missing creeps
for (const name in Memory.creeps) {
if (!(name in Game.creeps)) {
delete Memory.creeps[name];
}
}
// Create new creeps
const HARVESTER_CREEP_COUNT = 1;
const harvesterCreeps = Object.values(Game.creeps).filter((c) => c.memory.role === ROLE_HARVESTER);
if (harvesterCreeps.length < HARVESTER_CREEP_COUNT) {
const firstSpawn = _.first(Object.values(Game.spawns));
const name = uniqId(ROLE_HARVESTER);
const err = firstSpawn.spawnCreep([WORK, CARRY, MOVE], name, { memory: { role: ROLE_HARVESTER } as CreepMemory });
console.log('Err', err);
}
const UPGRADER_CREEP_COUNT = 2;
const upgraderCreeps = Object.values(Game.creeps).filter((c) => c.memory.role === ROLE_UPGRADER);
if (upgraderCreeps.length < UPGRADER_CREEP_COUNT) {
const firstSpawn = _.first(Object.values(Game.spawns));
const name = uniqId(ROLE_UPGRADER);
const err = firstSpawn.spawnCreep([WORK, CARRY, MOVE], name, { memory: { role: ROLE_UPGRADER } as CreepMemory });
console.log('Err', err);
}
const BUILDER_CREEP_COUNT = 2;
const builderCreeps = Object.values(Game.creeps).filter((c) => c.memory.role === ROLE_BUILDER);
if (builderCreeps.length < BUILDER_CREEP_COUNT) {
const firstSpawn = _.first(Object.values(Game.spawns));
const name = uniqId(ROLE_BUILDER);
const err = firstSpawn.spawnCreep([WORK, CARRY, MOVE], name, { memory: { role: ROLE_BUILDER } as CreepMemory });
console.log('Err', err);
}
// Process current creeps
for (let name in Game.creeps) {
const creep = Game.creeps[name];
if (creep.memory.role === ROLE_HARVESTER) {
runAsHarvester(creep);
}
if (creep.memory.role === ROLE_UPGRADER) {
runAsUpgrader(creep);
}
if (creep.memory.role === ROLE_BUILDER) {
runAsBuilder(creep);
}
}
});

20
src/types.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
// example declaration file - remove these and add your own custom typings
// memory extension samples
interface CreepMemory {
role: string;
room: string;
working: boolean;
}
interface Memory {
uuid: number;
log: any;
}
// `global` extension samples
declare namespace NodeJS {
interface Global {
log: any;
}
}

90
src/utils/ErrorMapper.ts Normal file
View File

@ -0,0 +1,90 @@
// tslint:disable:no-conditional-assignment
import { SourceMapConsumer } from 'source-map';
export class ErrorMapper {
// Cache consumer
private static _consumer?: SourceMapConsumer;
public static get consumer(): SourceMapConsumer {
if (this._consumer == null) {
this._consumer = new SourceMapConsumer(require('main.js.map'));
}
return this._consumer;
}
// Cache previously mapped traces to improve performance
public static cache: { [key: string]: string } = {};
/**
* Generates a stack trace using a source map generate original symbol names.
*
* WARNING - EXTREMELY high CPU cost for first call after reset - >30 CPU! Use sparingly!
* (Consecutive calls after a reset are more reasonable, ~0.1 CPU/ea)
*
* @param {Error | string} error The error or original stack trace
* @returns {string} The source-mapped stack trace
*/
public static sourceMappedStackTrace(error: Error | string): string {
const stack: string = error instanceof Error ? (error.stack as string) : error;
if (this.cache.hasOwnProperty(stack)) {
return this.cache[stack];
}
const re = /^\s+at\s+(.+?\s+)?\(?([0-z._\-\\\/]+):(\d+):(\d+)\)?$/gm;
let match: RegExpExecArray | null;
let outStack = error.toString();
while ((match = re.exec(stack))) {
if (match[2] === 'main') {
const pos = this.consumer.originalPositionFor({
column: parseInt(match[4], 10),
line: parseInt(match[3], 10)
});
if (pos.line != null) {
if (pos.name) {
outStack += `\n at ${pos.name} (${pos.source}:${pos.line}:${pos.column})`;
} else {
if (match[1]) {
// no original source file name known - use file name from given trace
outStack += `\n at ${match[1]} (${pos.source}:${pos.line}:${pos.column})`;
} else {
// no original source file name known or in given trace - omit name
outStack += `\n at ${pos.source}:${pos.line}:${pos.column}`;
}
}
} else {
// no known position
break;
}
} else {
// no more parseable lines
break;
}
}
this.cache[stack] = outStack;
return outStack;
}
public static wrapLoop(loop: () => void): () => void {
return () => {
try {
loop();
} catch (e) {
if (e instanceof Error) {
if ('sim' in Game.rooms) {
const message = `Source maps don't work in the simulator - displaying original error`;
console.log(`<span style='color:red'>${message}<br>${_.escape(e.stack)}</span>`);
} else {
console.log(`<span style='color:red'>${_.escape(this.sourceMappedStackTrace(e))}</span>`);
}
} else {
// can't handle it
throw e;
}
}
};
}
}

View File

@ -0,0 +1,59 @@
const { readFileSync } = require('fs');
const _ = require('lodash');
const { ScreepsServer, stdHooks } = require('screeps-server-mockup');
const DIST_MAIN_JS = 'dist/main.js';
/*
* Helper class for creating a ScreepsServer and resetting it between tests.
* See https://github.com/Hiryus/screeps-server-mockup for instructions on
* manipulating the terrain and game state.
*/
class IntegrationTestHelper {
private _server: any;
private _player: any;
get server() {
return this._server;
}
get player() {
return this._player;
}
async beforeEach() {
this._server = new ScreepsServer();
// reset world but add invaders and source keepers bots
await this._server.world.reset();
// create a stub world composed of 9 rooms with sources and controller
await this._server.world.stubWorld();
// add a player with the built dist/main.js file
const modules = {
main: readFileSync(DIST_MAIN_JS).toString(),
};
this._player = await this._server.world.addBot({ username: 'player', room: 'W0N1', x: 15, y: 15, modules });
// Start server
await this._server.start();
}
async afterEach() {
await this._server.stop();
}
}
beforeEach(async () => {
await helper.beforeEach();
});
afterEach(async () => {
await helper.afterEach();
});
before(() => {
stdHooks.hookWrite();
});
export const helper = new IntegrationTestHelper();

View File

@ -0,0 +1,18 @@
import {assert} from "chai";
import {helper} from "./helper";
describe("main", () => {
it("runs a server and matches the game tick", async function () {
for (let i = 1; i < 10; i += 1) {
assert.equal(await helper.server.world.gameTime, i);
await helper.server.tick();
}
});
it("writes and reads to memory", async function () {
await helper.player.console(`Memory.foo = 'bar'`);
await helper.server.tick();
const memory = JSON.parse(await helper.player.memory);
assert.equal(memory.foo, 'bar');
});
});

13
test/mocha.opts Normal file
View File

@ -0,0 +1,13 @@
--require test/setup-node.js
--require ts-node/register
--ui bdd
--reporter spec
--bail
--full-trace
--watch-extensions tsx,ts
--colors
--recursive
--timeout 5000
--exit

6
test/setup-node.js Normal file
View File

@ -0,0 +1,6 @@
//inject mocha globally to allow custom interface refer without direct import - bypass bundle issue
global._ = require('lodash');
global.mocha = require('mocha');
global.chai = require('chai');
global.sinon = require('sinon');
global.chai.use(require('sinon-chai'));

25
test/unit/main.test.ts Normal file
View File

@ -0,0 +1,25 @@
import {assert} from "chai";
import {loop} from "../../src/main";
import {Game, Memory} from "./mock"
describe("main", () => {
before(() => {
// runs before all test in this block
});
beforeEach(() => {
// runs before each test in this block
// @ts-ignore : allow adding Game to global
global.Game = _.clone(Game);
// @ts-ignore : allow adding Memory to global
global.Memory = _.clone(Memory);
});
it("should export a loop function", () => {
assert.isTrue(typeof loop === "function");
});
it("should return void when called with no context", () => {
assert.isUndefined(loop());
});
});

10
test/unit/mock.ts Normal file
View File

@ -0,0 +1,10 @@
export const Game = {
creeps: [],
rooms: [],
spawns: {},
time: 12345
};
export const Memory = {
creeps: []
};

21
tools/node Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
set -eu
source .env
TTY=
if [ -t 1 ] ; then
TTY=--tty
fi
docker run \
--rm \
--interactive \
${TTY} \
--init \
--user "$(id -u):$(id -g)" \
--volume "$PWD:/app" \
--workdir /app \
${NODE_IMAGE} \
node "$@"

27
tools/npm Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
set -eu
source .env
HOST_CACHE_DIR=$PWD/var/docker-cache/.npm
CONTAINER_CACHE_DIR=/tmp/.npm
mkdir -p ${HOST_CACHE_DIR}
TTY=
if [ -t 1 ] ; then
TTY=--tty
fi
docker run \
--rm \
--interactive \
${TTY} \
--init \
--user "$UID:$(id -g)" \
--volume "$PWD:/app" \
--env npm_config_cache="${CONTAINER_CACHE_DIR}" \
--workdir /app \
${NODE_IMAGE} \
npm "$@"

16
tools/tsc Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -eu
source .env
docker run \
--rm \
--interactive \
--tty \
--init \
--user "$(id -u):$(id -g)" \
--volume "$PWD:/app" \
--workdir /app \
${NODE_IMAGE} \
./node_modules/.bin/tsc "$@"

26
tools/yarn Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
set -eu
source .env
HOST_CACHE_DIR=$PWD/var/docker-cache/.npm
CONTAINER_CACHE_DIR=/tmp/.npm
mkdir -p ${HOST_CACHE_DIR}
TTY=
if [ -t 1 ] ; then
TTY=--tty
fi
docker run \
--rm \
--interactive \
${TTY} \
--init \
--user "$UID:$(id -g)" \
--volume "$PWD:/app" \
--workdir /app \
${NODE_IMAGE} \
yarn "$@"

19
tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "esnext",
"lib": ["esnext"],
"target": "es2017",
"moduleResolution": "Node",
"outDir": "dist",
"baseUrl": "src/",
"sourceMap": true,
"strict": true,
"experimentalDecorators": true,
"noImplicitReturns": true,
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": false
},
"exclude": [
"node_modules"
]
}

View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"module": "esnext",
"lib": ["esnext"],
"target": "es5",
"moduleResolution": "Node",
"outDir": "dist",
"baseUrl": "src/",
"sourceMap": true,
"strict": true,
"experimentalDecorators": true,
"noImplicitReturns": true,
"noImplicitAny": false,
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": false
},
"exclude": [
"node_modules"
]
}

21
tslint.json Normal file
View File

@ -0,0 +1,21 @@
{
"rulesDirectory": "tslint-plugin-prettier",
"extends" : [
"tslint:recommended",
"tslint-config-prettier"
],
"rules": {
"forin": false,
"interface-name": [true, "never-prefix"],
"member-ordering": [false],
"no-console": [false],
"no-namespace": [true, "allow-declarations"],
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-pascal-case",
"allow-leading-underscore"
]
}
}