Progress on incoming event handling

main
Mari 1 year ago
parent d312577036
commit 381ca59c82
  1. 46
      .eslintrc.cjs
  2. 46
      .eslintrc.js
  3. 6
      .idea/jsLibraryMappings.xml
  4. 30
      slash-up.config.js
  5. 2
      src/game/dieState.spec.ts
  6. 130
      src/game/dieState.ts
  7. 53
      src/game/gameEvent.ts
  8. 162
      src/game/gameState.ts
  9. 198
      src/game/handleEvent.ts
  10. 3
      src/util/precondition.ts
  11. 62
      webpack.config.js

@ -0,0 +1,46 @@
module.exports = {
env: {
commonjs: true,
es6: true,
browser: true,
},
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
globals: {
DISCORD_APP_ID: true,
DISCORD_PUBLIC_KEY: true,
DISCORD_BOT_TOKEN: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
rules: {
'prettier/prettier': 'warn',
'no-cond-assign': [2, 'except-parens'],
'no-unused-vars': 0,
'@typescript-eslint/no-unused-vars': 1,
'no-empty': [
'error',
{
allowEmptyCatch: true,
},
],
'prefer-const': [
'warn',
{
destructuring: 'all',
},
],
'spaced-comment': 'warn',
},
overrides: [
{
files: ['slash-up.config.js', 'webpack.config.js'],
env: {
node: true,
},
},
],
}

@ -1,46 +0,0 @@
module.exports = {
env: {
commonjs: true,
es6: true,
browser: true
},
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
globals: {
DISCORD_APP_ID: true,
DISCORD_PUBLIC_KEY: true,
DISCORD_BOT_TOKEN: true
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 6,
sourceType: 'module'
},
plugins: ['@typescript-eslint'],
rules: {
'prettier/prettier': 'warn',
'no-cond-assign': [2, 'except-parens'],
'no-unused-vars': 0,
'@typescript-eslint/no-unused-vars': 1,
'no-empty': [
'error',
{
allowEmptyCatch: true
}
],
'prefer-const': [
'warn',
{
destructuring: 'all'
}
],
'spaced-comment': 'warn'
},
overrides: [
{
files: ['slash-up.config.js', 'webpack.config.js'],
env: {
node: true
}
}
]
};

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

@ -2,18 +2,18 @@
// Make sure to fill in "token" and "applicationId" before using.
// You can also use environment variables from the ".env" file if any.
module.exports = {
// The Token of the Discord bot
token: process.env.DISCORD_BOT_TOKEN,
// The Application ID of the Discord bot
applicationId: process.env.DISCORD_APP_ID,
// This is where the path to command files are, .ts files are supported!
commandPath: './src/commands',
// You can use different environments with --env (-e)
env: {
development: {
// The "globalToGuild" option makes global commands sync to the specified guild instead.
globalToGuild: process.env.DEVELOPMENT_GUILD_ID
}
}
};
export default {
// The Token of the Discord bot
token: process.env.DISCORD_BOT_TOKEN,
// The Application ID of the Discord bot
applicationId: process.env.DISCORD_APP_ID,
// This is where the path to command files are, .ts files are supported!
commandPath: './src/commands',
// You can use different environments with --env (-e)
env: {
development: {
// The "globalToGuild" option makes global commands sync to the specified guild instead.
globalToGuild: process.env.DEVELOPMENT_GUILD_ID,
},
},
}

@ -1,5 +1,5 @@
import { describe, expect, test } from '@jest/globals'
import { DieState, isHeldState, isSelectedState, setDeselected, setSelected, toggleSelected } from './gameData'
import { DieState, isHeldState, isSelectedState, setDeselected, setSelected, toggleSelected } from './dieState'
describe('isHeldState', () => {
test.each<[DieState, boolean]>([

@ -0,0 +1,130 @@
export enum DieFace {
FAIL = 'F',
STOP = 'S',
WILD = '*',
BONUS = 'B',
USELESS = 'X',
SCORE_0 = '0',
SCORE_1 = '1',
SCORE_2 = '2',
SCORE_3 = '3',
SCORE_4 = '4',
SCORE_5 = '5',
SCORE_6 = '6',
SCORE_7 = '7',
SCORE_8 = '8',
SCORE_9 = '9',
SCORE_10 = '10',
}
export type SpecialDie = DieFace.FAIL | DieFace.STOP | DieFace.WILD | DieFace.BONUS | DieFace.USELESS
export type ScoringDie =
| DieFace.SCORE_0
| DieFace.SCORE_1
| DieFace.SCORE_2
| DieFace.SCORE_3
| DieFace.SCORE_4
| DieFace.SCORE_5
| DieFace.SCORE_6
| DieFace.SCORE_7
| DieFace.SCORE_8
| DieFace.SCORE_9
| DieFace.SCORE_10
type DieFaceCount = {
[v in DieFace]?: number
}
export enum DieState {
ROLLED = 'rolled',
SELECTED = 'selected',
HELD = 'held',
HELD_SELECTED = 'held_selected',
SCORED = 'scored',
}
export function isHeldState(d: DieState): d is DieState.HELD | DieState.HELD_SELECTED {
return d === DieState.HELD || d === DieState.HELD_SELECTED
}
export function isSelectedState(d: DieState): d is DieState.SELECTED | DieState.HELD_SELECTED {
return d === DieState.SELECTED || d === DieState.HELD_SELECTED
}
export function setSelected(d: DieState): DieState.SELECTED | DieState.HELD_SELECTED | DieState.SCORED {
switch (d) {
case DieState.ROLLED:
return DieState.SELECTED
case DieState.HELD:
return DieState.HELD_SELECTED
case DieState.SELECTED:
case DieState.HELD_SELECTED:
case DieState.SCORED:
return d
}
}
export function setDeselected(d: DieState): DieState.ROLLED | DieState.HELD | DieState.SCORED {
switch (d) {
case DieState.SELECTED:
return DieState.ROLLED
case DieState.HELD_SELECTED:
return DieState.HELD
case DieState.ROLLED:
case DieState.HELD:
case DieState.SCORED:
return d
}
}
export function toggleSelected(d: DieState): DieState {
switch (d) {
case DieState.ROLLED:
return DieState.SELECTED
case DieState.SELECTED:
return DieState.ROLLED
case DieState.HELD:
return DieState.HELD_SELECTED
case DieState.HELD_SELECTED:
return DieState.HELD
case DieState.SCORED:
return DieState.SCORED
}
}
export interface DieResult {
readonly type: DieType
readonly face: DieFace
readonly state: DieState
}
export interface DieType {
// The name of this die type
readonly name: string
// The faces of this die type; faces may be repeated to increase their odds
readonly faces: readonly DieFace[]
}
export interface DiceCombo {
// The name of this combo, for the UI
readonly name: string
// How many points this combo is worth
readonly points: number
// Which dice are needed to satisfy this combo
readonly dice: readonly ScoringDie[]
// How many dice can be replaced for the combo
readonly wildMaxDice?: number
}
export interface DiceComboResult {
// combo used
readonly combo: DiceCombo
// total number of points
readonly points: number
// number of dice used
readonly usesDice: DieFaceCount
// number of wild dice used
readonly wildDice: number
// number of bonus dice used
readonly bonusDice: number
}

@ -1,30 +1,40 @@
import { DieResult, GameData } from './gameData'
import { DiceCombo, DieResult } from './dieState'
export enum IncomingEventType {
// Chooses the action to be performed for this round but does not lock it in until the player rolls.
// Valid during GameState.ROUND_START
SelectAction = 'select_action',
// Dice buttons toggle the selection of that die
// Valid during GameState.TURN_ROLLED
ToggleSelectDie = 'toggle_select_die',
// Button 1 is Select All/Deselect All
// Select All appears if no dice are selected
// Deselect All displays if at least one die is selected
// Both valid during GameState.TURN_ROLLED
SelectAllDice = 'select_all_dice',
DeselectAllDice = 'deselect_all_dice',
// Button 2 is Hold/Release selected dice
// If only held dice are selected, Release appears
// For selections with at least one unheld die, Hold appears
// Both valid during GameState.TURN_ROLLED
HoldSelectedDice = 'hold_selected_dice',
ReleaseSelectedDice = 'release_selected_dice',
// Button 3 is End Turn/Score Selected Dice
// End Turn appears if at least one Stop die is selected, but is only enabled if sufficient Stop dice are selected
// and no non-Stop dice are selected
// Score Selected Dice appears otherwise, but is only enabled if at least one scoreable die is selected and no
// non-scoreable dice are selected
// End Turn appears by default, but is only enabled if sufficient Stop dice are in the current roll or if the turn
// has been failed
// Score Selected Dice appears when any dice are selected, but is only enabled if at least one scoreable die is
// selected and no non-scoreable dice are selected
// Both appear during GameState.TURN_ROLLED
// End Turn also appears and must be pressed to move to the next round (or end the game) in GameState.TURN_FAILED
EndTurn = 'end_turn',
ScoreSelectedDice = 'score_selected_dice',
// Button 4 is Roll Dice
// It's always visible, but is disabled if there are no unheld dice
// It's always visible, but is disabled if there are no unheld dice or if the action has not been chosen
// Valid during GameState.ROUND_START, GameState.TURN_START, and GameState.TURN_ROLLED
RollDice = 'roll_dice',
// Abort ends the game without a victor
// Valid during any state except GameState.ABORTED or GameState.VICTORY
Abort = 'abort',
}
export interface IncomingBaseEvent {
@ -46,6 +56,7 @@ export interface IncomingSimpleEvent extends IncomingBaseEvent {
| IncomingEventType.EndTurn
| IncomingEventType.DeselectAllDice
| IncomingEventType.RollDice
| IncomingEventType.Abort
}
export type IncomingEvent = IncomingIndexedEvent | IncomingSimpleEvent
@ -74,6 +85,7 @@ export enum OutgoingEventType {
ScoreDice = 'score_dice',
GainFailures = 'gain_failures',
SafeEndTurn = 'safe_end_turn',
LoseRound = 'lose_round',
LoseGame = 'lose_game',
@ -83,11 +95,6 @@ export enum OutgoingEventType {
StartRound = 'new_round',
}
export enum EventTarget {
Top = 'top',
Bottom = 'bottom',
}
export enum TextSender {
Top = 'top',
Bottom = 'bottom',
@ -114,6 +121,7 @@ export interface OutgoingBaseTargetedEvent extends OutgoingBaseEvent {
| OutgoingEventType.LoseGame
| OutgoingEventType.ScoreDice
| OutgoingEventType.GainFailures
| OutgoingEventType.SafeEndTurn
| OutgoingEventType.StartRoll
| OutgoingEventType.StartTurn
| OutgoingEventType.StartRound
@ -121,7 +129,11 @@ export interface OutgoingBaseTargetedEvent extends OutgoingBaseEvent {
}
export interface OutgoingSimpleTargetedEvent extends OutgoingBaseTargetedEvent {
readonly type: OutgoingEventType.LoseGame | OutgoingEventType.StartTurn | OutgoingEventType.StartRound
readonly type:
| OutgoingEventType.LoseGame
| OutgoingEventType.StartTurn
| OutgoingEventType.StartRound
| OutgoingEventType.SafeEndTurn
}
export interface OutgoingDamageEvent extends OutgoingBaseTargetedEvent {
@ -137,12 +149,13 @@ export interface OutgoingDamageEvent extends OutgoingBaseTargetedEvent {
export interface OutgoingStartRollEvent extends OutgoingBaseTargetedEvent {
readonly type: OutgoingEventType.StartRoll
readonly newDice: DieResult[]
readonly newDice: readonly DieResult[]
}
export interface OutgoingScoreDiceEvent extends OutgoingBaseTargetedEvent {
readonly type: OutgoingEventType.ScoreDice
readonly scoredDice: DieResult[]
readonly scoredCombo: DiceCombo
readonly scoredDice: readonly DieResult[]
readonly scoreDelta: number
}
@ -153,16 +166,16 @@ export type OutgoingEvent =
| OutgoingStartRollEvent
| OutgoingScoreDiceEvent
export interface BaseEventResult {
readonly newState: GameData
export interface BaseEventResult<T> {
readonly newState: T
}
export interface SuccessResult extends BaseEventResult {
readonly events: OutgoingEvent[]
export interface SuccessResult<T> extends BaseEventResult<T> {
readonly events: readonly OutgoingEvent[]
}
export interface FailedResult extends BaseEventResult {
export interface FailedResult<T> extends BaseEventResult<T> {
readonly error: GameError
}
export type EventResult = SuccessResult | FailedResult
export type EventResult<T> = SuccessResult<T> | FailedResult<T>

@ -1,88 +1,5 @@
import { RenderableText } from '../util/renderableText'
export enum DieFace {
FAIL = 'F',
STOP = 'S',
SCORE_0 = '0',
SCORE_1 = '1',
SCORE_2 = '2',
SCORE_3 = '3',
SCORE_4 = '4',
SCORE_5 = '5',
WILD = '*',
HEART = '<3',
QUESTION = '?',
}
export enum DieState {
ROLLED = 'rolled',
SELECTED = 'selected',
HELD = 'held',
HELD_SELECTED = 'held_selected',
SCORED = 'scored',
}
export function isHeldState(d: DieState): d is DieState.HELD | DieState.HELD_SELECTED {
return d === DieState.HELD || d === DieState.HELD_SELECTED
}
export function isSelectedState(d: DieState): d is DieState.SELECTED | DieState.HELD_SELECTED {
return d === DieState.SELECTED || d === DieState.HELD_SELECTED
}
export function setSelected(d: DieState): DieState.SELECTED | DieState.HELD_SELECTED | DieState.SCORED {
switch (d) {
case DieState.ROLLED:
return DieState.SELECTED
case DieState.HELD:
return DieState.HELD_SELECTED
case DieState.SELECTED:
case DieState.HELD_SELECTED:
case DieState.SCORED:
return d
}
}
export function setDeselected(d: DieState): DieState.ROLLED | DieState.HELD | DieState.SCORED {
switch (d) {
case DieState.SELECTED:
return DieState.ROLLED
case DieState.HELD_SELECTED:
return DieState.HELD
case DieState.ROLLED:
case DieState.HELD:
case DieState.SCORED:
return d
}
}
export function toggleSelected(d: DieState): DieState {
switch (d) {
case DieState.ROLLED:
return DieState.SELECTED
case DieState.SELECTED:
return DieState.ROLLED
case DieState.HELD:
return DieState.HELD_SELECTED
case DieState.HELD_SELECTED:
return DieState.HELD
case DieState.SCORED:
return DieState.SCORED
}
}
export interface DieResult {
readonly type: DieType
readonly face: DieFace
readonly state: DieState
}
export interface DieType {
// The name of this die type
readonly name: string
// The faces of this die type; faces may be repeated to increase their odds
readonly faces: readonly DieFace[]
}
import { DiceCombo, DieResult, DieType } from './dieState'
export interface AIWeights {
// How much this AI likes to choose actions that increase the damage bonus
@ -165,11 +82,13 @@ export interface Difficulty {
readonly shortDescription?: string
// Description of the difficulty when selected
readonly description?: string
// The player who should start in this difficulty; defaults to random
readonly startingPlayer?: PlayerSide | null
// Starting values for the top
readonly topStats: PlayerStartingState
// Starting values for the bottom
readonly bottomStats: PlayerStartingState
// Starting values for each character
readonly stats: {
readonly [x in PlayerSide]: PlayerStartingState
}
}
export interface PlayerText {
@ -198,6 +117,8 @@ export interface GameTheme {
readonly shortDescription: string
readonly description: string
readonly diceCombos: readonly DiceCombo[]
readonly actions: readonly GameAction[]
readonly difficulties: readonly Difficulty[]
@ -205,8 +126,10 @@ export interface GameTheme {
readonly commonText?: { readonly [key: string]: RenderableText | undefined }
readonly narratorName: string
readonly topText: PlayerText
readonly bottomText: PlayerText
readonly text: {
readonly [x in PlayerSide]: PlayerText
}
}
export interface TriggeredText {
@ -257,10 +180,10 @@ export interface ActionText {
}
export interface GameAction {
// The text from the top's perspective, and for the top's actions
readonly topText: ActionText
// The text from the bottom's perspective, and for the bottom's actions
readonly bottomText: ActionText
// The text from each player's perspective.
readonly text: {
readonly [x in PlayerSide]: ActionText
}
// The dice that are rolled for this action
readonly dice: readonly DieType[]
@ -276,6 +199,11 @@ export interface GameAction {
readonly canFinishBottom: boolean
}
export enum PlayerSide {
TOP = 'top',
BOTTOM = 'bottom',
}
export interface PlayerState {
// This player's name
readonly name: string
@ -301,49 +229,45 @@ export interface PlayerState {
readonly timesRecovered: number
}
export enum GameState {
ONGOING = 'ongoing',
export enum GamePhase {
ROUND_START = 'round_start',
TURN_START = 'turn_start',
TURN_ROLLED = 'turn_rolled',
TURN_FAILED = 'turn_failed',
ABORTED = 'aborted',
TOP_WINS = 'top_wins',
BOTTOM_WINS = 'bottom_wins',
VICTORY = 'victory',
}
export interface GameData {
// The version of the serialization format this data was saved with.
readonly version: number
// Whether the game is still ongoing, or if not, who won (if anyone).
readonly gameState: GameState
// The state of the top player
readonly top: PlayerState
// The state of the bottom player
readonly bottom: PlayerState
export interface GameState {
// The theme that defines this game for the players.
readonly theme: GameTheme
// The difficulty selected for this game.
readonly difficulty: Difficulty
// If true, the top chose/is choosing the action this round.
readonly isTopRound: boolean
// The action chosen for this round, or null if the current player has not chosen an action yet.
// The action is locked in if lastRoll is not null or lastTurnTotal is not 0.
readonly action: GameAction | null
// The current phase of the game.
readonly gamePhase: GamePhase
// The owner of the current phase, or null if the phase is Aborted
readonly phaseOwner: PlayerSide | null
// The state of each player in the game.
readonly players: {
readonly [x in PlayerSide]: PlayerState
}
// If true, the top is rolling this turn.
readonly isTopTurn: boolean
// The action chosen for this round. Defaults to the first action in the list.
readonly action: GameAction
// The total for the previous turn, or 0 if the current player is taking the first turn
// If the current player fails when the turn total is 0, they take the penalty and pass the initiative without taking damage.
// This can still end the game if the current action is capable of ending the game for this player and
// this player has more damage than their maximum.
readonly lastTurnTotal: number
// The dice available for the current turn, or null if the current player has not rolled yet this turn.
readonly lastRoll: readonly DieResult[] | null
// The dice available for the current turn
readonly lastRoll: readonly DieResult[]
// The total value of the selected dice, or 0 if the selected dice cannot be scored.
readonly selectedDiceValue: number
// Whether the selected dice are sufficient to end the turn.
readonly selectedDiceEndTurn: boolean
// The number of dice that have come up failures so far in the current turn
// The number of dice that have come up failures so far in the current turn, including the lastRoll.
readonly countedFails: number
// The total of the dice that have been scored so far this turn
readonly currentTurnTotal: number

@ -0,0 +1,198 @@
import { ErrorType, EventResult, IncomingEvent, IncomingEventType } from './gameEvent'
import { GamePhase, GameState } from './gameState'
import { DieResult, DieState, isSelectedState, setDeselected, setSelected, toggleSelected } from './dieState'
import { isValidIndex } from '../util/precondition'
export function handleEvent(state: GameState, event: IncomingEvent): EventResult<GameState> {
try {
switch (event.type) {
case IncomingEventType.SelectAction:
return handleSelectActionEvent<GameState>(state, event)
case IncomingEventType.ToggleSelectDie:
return handleToggleSelectIndexEvent<DieResult, GameState>(state, event)
case IncomingEventType.SelectAllDice:
return handleSelectAllEvent<DieResult, GameState>(state, true)
case IncomingEventType.DeselectAllDice:
return handleSelectAllEvent<DieResult, GameState>(state, false)
case IncomingEventType.HoldSelectedDice:
return handleHoldDiceEvent<DieResult, GameState>(state, true)
case IncomingEventType.ReleaseSelectedDice:
return handleHoldDiceEvent<DieResult, GameState>(state, false)
case IncomingEventType.Abort:
return handleAbortEvent<GameState>(state)
default:
return {
newState: state,
error: {
type: ErrorType.UnexpectedError,
message: 'Event type not yet implemented',
},
}
}
} catch (e) {
return {
newState: state,
error: {
type: ErrorType.UnexpectedError,
message: `Failed handling event: ${e}`,
},
}
}
}
export interface SelectActionState<A> {
readonly gamePhase: GamePhase
readonly theme: {
readonly actions: readonly A[]
}
readonly action: A
}
export function handleSelectActionEvent<T extends SelectActionState<unknown>>(
state: T,
{ index }: { readonly index: number },
): EventResult<T> {
if (state.gamePhase !== GamePhase.ROUND_START) {
return {
error: {
type: ErrorType.NotValidRightNow,
message: `You can only change actions when the turn has not started yet.`,
},
newState: state,
}
}
if (index < 0 || index >= state.theme.actions.length || index !== Math.floor(index)) {
return {
error: {
type: ErrorType.InvalidIndex,
message: `Select an integer action index between 0 (inclusive) and ${state.theme.actions.length} (exclusive)`,
},
newState: state,
}
}
return {
newState: {
...state,
action: state.theme.actions[index],
},
events: [],
}
}
export interface DiceStatesState<D extends { readonly state: DieState }> {
readonly gamePhase: GamePhase
readonly lastRoll: readonly D[]
}
export function handleToggleSelectIndexEvent<D extends { readonly state: DieState }, T extends DiceStatesState<D>>(
state: T,
{ index }: { readonly index: number },
): EventResult<T> {
if (state.gamePhase !== GamePhase.TURN_ROLLED) {
return {
error: {
type: ErrorType.NotValidRightNow,
message: `You can only toggle the selected state of a die when dice have been rolled.`,
},
newState: state,
}
}
if (!isValidIndex(index, state.lastRoll.length)) {
return {
newState: state,
error: {
type: ErrorType.InvalidIndex,
message: `Select an integer action index between 0 (inclusive) and ${state.lastRoll.length} (exclusive)`,
},
}
}
return {
newState: {
...state,
lastRoll: [
...state.lastRoll.slice(0, index),
{
...state.lastRoll[index],
state: toggleSelected(state.lastRoll[index].state),
},
...state.lastRoll.slice(index + 1),
],
},
events: [],
}
}
function handleSelectAllEvent<D extends { readonly state: DieState }, T extends DiceStatesState<D>>(
state: T,
select: boolean,
): EventResult<T> {
if (state.gamePhase !== GamePhase.TURN_ROLLED) {
return {
error: {
type: ErrorType.NotValidRightNow,
message: `You can only change the selected state of the dice when dice have been rolled.`,
},
newState: state,
}
}
return {
newState: {
...state,
lastRoll: state.lastRoll.map<D>((x) => ({
...x,
state: select ? setSelected(x.state) : setDeselected(x.state),
})),
},
events: [],
}
}
function handleHoldDiceEvent<D extends { readonly state: DieState }, T extends DiceStatesState<D>>(
state: T,
hold: boolean,
): EventResult<T> {
if (state.gamePhase !== GamePhase.TURN_ROLLED) {
return {
error: {
type: ErrorType.NotValidRightNow,
message: `You can only toggle the hold state of dice when dice have been rolled.`,
},
newState: state,
}
}
return {
newState: {
...state,
lastRoll: state.lastRoll.map<D>((v) => {
if (isSelectedState(v.state)) {
return {
...v,
state: hold ? DieState.HELD_SELECTED : DieState.SELECTED,
}
} else {
return v
}
}),
},
events: [],
}
}
function handleAbortEvent<T extends { readonly gamePhase: GamePhase }>(state: T): EventResult<T> {
if (state.gamePhase === GamePhase.ABORTED || state.gamePhase === GamePhase.VICTORY) {
return {
error: {
type: ErrorType.NotValidRightNow,
message: `The game is already over.`,
},
newState: state,
}
}
return {
newState: {
...state,
gamePhase: GamePhase.ABORTED,
},
events: [],
}
}

@ -0,0 +1,3 @@
export function isValidIndex(index: number, length: number): boolean {
return Number.isInteger(index) && index >= 0 && index < length
}

@ -1,32 +1,32 @@
const path = require('path');
import { join } from 'path'
module.exports = {
output: {
filename: 'worker.js',
path: path.join(__dirname, 'dist')
},
mode: 'production',
resolve: {
extensions: ['.ts', '.tsx', '.js'],
plugins: [],
fallback: {
zlib: false,
https: false,
fs: false,
fastify: false,
express: false,
path: false
}
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
]
}
};
export default {
output: {
filename: 'worker.js',
path: join(__dirname, 'dist'),
},
mode: 'production',
resolve: {
extensions: ['.ts', '.tsx', '.js'],
plugins: [],
fallback: {
zlib: false,
https: false,
fs: false,
fastify: false,
express: false,
path: false,
},
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true,
},
},
],
},
}

Loading…
Cancel
Save