chore: initial
This commit is contained in:
commit
f6ca8e7925
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
dist
|
||||||
|
node_modules
|
16
babel.config.js
Normal file
16
babel.config.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module.exports = function (api) {
|
||||||
|
api.cache(true);
|
||||||
|
|
||||||
|
const presets = [
|
||||||
|
'@babel/preset-react',
|
||||||
|
'@babel/preset-env',
|
||||||
|
];
|
||||||
|
const plugins = [
|
||||||
|
'@babel/plugin-proposal-object-rest-spread',
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
presets,
|
||||||
|
plugins
|
||||||
|
};
|
||||||
|
}
|
27
client/index.html
Normal file
27
client/index.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Avocado</title>
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
background-color: #333333;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.app, canvas {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
90
client/index.js
Normal file
90
client/index.js
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import {create as createClient} from '@avocado/client';
|
||||||
|
import {create as createEntity, EntityList} from '@avocado/entity';
|
||||||
|
import {ActionRegistry} from '@avocado/input';
|
||||||
|
import {AnimationView, Color, Container, Primitives, Renderer} from '@avocado/graphics';
|
||||||
|
import {StateSynchronizer} from '@avocado/state';
|
||||||
|
import {Animation} from '@avocado/timing';
|
||||||
|
|
||||||
|
const stage = new Container();
|
||||||
|
|
||||||
|
const entityList = new EntityList();
|
||||||
|
const stateSynchronizer = new StateSynchronizer({
|
||||||
|
entityList,
|
||||||
|
});
|
||||||
|
|
||||||
|
entityList.on('entityAdded', (entity) => {
|
||||||
|
if ('container' in entity) {
|
||||||
|
stage.addChild(entity.container);
|
||||||
|
// Debug circle.
|
||||||
|
const primitives = new Primitives();
|
||||||
|
primitives.drawCircle(
|
||||||
|
[0, 0],
|
||||||
|
16,
|
||||||
|
Primitives.lineStyle(new Color(255, 0, 255), 1)
|
||||||
|
);
|
||||||
|
primitives.zIndex = 1;
|
||||||
|
entity.container.addChild(primitives);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
entityList.on('entityRemoved', (entity) => {
|
||||||
|
if ('container' in entity) {
|
||||||
|
stage.removeChild(entity.container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionRegistry = new ActionRegistry();
|
||||||
|
actionRegistry.mapKeysToActions({
|
||||||
|
'w': 'MoveUp',
|
||||||
|
'a': 'MoveLeft',
|
||||||
|
's': 'MoveDown',
|
||||||
|
'd': 'MoveRight',
|
||||||
|
});
|
||||||
|
let actionState = actionRegistry.state();
|
||||||
|
|
||||||
|
const socket = createClient(window.location.href);
|
||||||
|
const renderer = new Renderer([1280, 720]);
|
||||||
|
const appNode = document.querySelector('.app');
|
||||||
|
appNode.appendChild(renderer.element);
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.dispose(() => {
|
||||||
|
appNode.removeChild(renderer.element);
|
||||||
|
stage.destroy();
|
||||||
|
renderer.destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input.
|
||||||
|
actionRegistry.listen();
|
||||||
|
// Messages sent.
|
||||||
|
const handle = setInterval(() => {
|
||||||
|
if (actionState !== actionRegistry.state()) {
|
||||||
|
actionState = actionRegistry.state();
|
||||||
|
socket.send({
|
||||||
|
type: 'input',
|
||||||
|
payload: actionState.toJS()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000 / 60);
|
||||||
|
// Messages received.
|
||||||
|
let dirty = false;
|
||||||
|
function onMessage({type, payload}) {
|
||||||
|
switch (type) {
|
||||||
|
case 'state-update':
|
||||||
|
stateSynchronizer.acceptStateChange(payload);
|
||||||
|
dirty = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.on('message', onMessage);
|
||||||
|
// Render.
|
||||||
|
function render() {
|
||||||
|
stage.tick();
|
||||||
|
if (!dirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderer.render(stage);
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
const renderHandle = setInterval(render, 1000 / 60);
|
34
common-mistakes.md
Normal file
34
common-mistakes.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
```
|
||||||
|
webpack:///../lib/@avocado/packages/entity/trait.js?:29
|
||||||
|
entity.on("".concat(type, ".trait-").concat(traitType), listeners[type], this);
|
||||||
|
```
|
||||||
|
|
||||||
|
This is because you subclassed `Trait` but forgot to pass through the arguments
|
||||||
|
from your subclass constructor to the superclass constructor. Always default
|
||||||
|
to writing your Trait definition like:
|
||||||
|
|
||||||
|
```
|
||||||
|
class MyTrait extends Trait {
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All trait initialization should be done in `initialize`.
|
||||||
|
|
||||||
|
If you need really need to access arguments in your subclass constructor, opt
|
||||||
|
to pass along `arguments` instead, such as:
|
||||||
|
|
||||||
|
```
|
||||||
|
class MyTrait extends Trait {
|
||||||
|
|
||||||
|
constructor(entity, params, state) {
|
||||||
|
super(...arguments);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
34
generate-traits.js
Normal file
34
generate-traits.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const glob = require('glob');
|
||||||
|
|
||||||
|
// Dynamically require all traits.
|
||||||
|
module.exports = (source) => {
|
||||||
|
const modeModulesPath = path.resolve(
|
||||||
|
__dirname, 'node_modules',
|
||||||
|
);
|
||||||
|
const traitPaths = [
|
||||||
|
path.resolve(
|
||||||
|
modeModulesPath, '@avocado', 'entity', 'traits', '*.js',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const files = glob.sync(traitPaths.join());
|
||||||
|
const modules = files.map((file) => {
|
||||||
|
let dirname = path.dirname(file);
|
||||||
|
dirname = dirname.replace(`${modeModulesPath}/`, '');
|
||||||
|
const basename = path.basename(file, '.js');
|
||||||
|
return `${dirname}/${basename}`;
|
||||||
|
});
|
||||||
|
const defs = modules.map((module_) => {
|
||||||
|
const basename = path.basename(module_);
|
||||||
|
const parts = basename.split('-');
|
||||||
|
const className = parts.reduce((className, part) => {
|
||||||
|
const firstLetter = part.charAt(0).toUpperCase();
|
||||||
|
const rest = part.substr(1).toLowerCase();
|
||||||
|
return className + firstLetter + rest;
|
||||||
|
}, '');
|
||||||
|
return `import {${className}} from '${module_}';\nregisterTrait(${className});\n`;
|
||||||
|
});
|
||||||
|
defs.unshift(`import {registerTrait} from '@avocado/entity/trait-registry';\n`);
|
||||||
|
return defs.join('\n');
|
||||||
|
}
|
40
package.json
Normal file
40
package.json
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"name": "avocado-examples",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "cha0s",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "7.3.4",
|
||||||
|
"@babel/plugin-proposal-object-rest-spread": "7.3.4",
|
||||||
|
"@babel/polyfill": "7.2.5",
|
||||||
|
"@babel/preset-env": "7.3.4",
|
||||||
|
"@babel/preset-react": "7.0.0",
|
||||||
|
"babel-loader": "8.0.5",
|
||||||
|
"html-webpack-plugin": "3.2.0",
|
||||||
|
"start-server-webpack-plugin": "2.2.5",
|
||||||
|
"webpack": "4.29.6",
|
||||||
|
"webpack-cli": "3.2.3",
|
||||||
|
"webpack-dev-server": "3.2.1",
|
||||||
|
"webpack-node-externals": "1.7.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"client": "webpack-dev-server --config webpack.client.config.js",
|
||||||
|
"server": "webpack --config webpack.server.config.js",
|
||||||
|
"start": "webpack --config webpack.server.config.js && webpack-dev-server --config webpack.client.config.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@avocado/client": "1.x",
|
||||||
|
"@avocado/core": "1.x",
|
||||||
|
"@avocado/entity": "1.x",
|
||||||
|
"@avocado/graphics": "1.x",
|
||||||
|
"@avocado/input": "1.x",
|
||||||
|
"@avocado/mixins": "1.x",
|
||||||
|
"@avocado/resource": "1.x",
|
||||||
|
"@avocado/server": "1.x",
|
||||||
|
"@avocado/state": "1.x",
|
||||||
|
"@avocado/timing": "1.x",
|
||||||
|
"glob": "^7.1.3",
|
||||||
|
"immutablediff": "^0.4.4",
|
||||||
|
"source-map-support": "^0.5.11"
|
||||||
|
}
|
||||||
|
}
|
0
register-traits.js
Normal file
0
register-traits.js
Normal file
7
resource/idle.animation.json
Normal file
7
resource/idle.animation.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"frameRate": 0.1,
|
||||||
|
"frameCount": 8,
|
||||||
|
"frameSize": [128, 128],
|
||||||
|
"directionCount": 4,
|
||||||
|
"imageUri": "/idle.png"
|
||||||
|
}
|
BIN
resource/idle.png
Normal file
BIN
resource/idle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
7
resource/moving.animation.json
Normal file
7
resource/moving.animation.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"frameRate": 0.1,
|
||||||
|
"frameCount": 8,
|
||||||
|
"frameSize": [128, 128],
|
||||||
|
"directionCount": 4,
|
||||||
|
"imageUri": "/moving.png"
|
||||||
|
}
|
BIN
resource/moving.png
Normal file
BIN
resource/moving.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
0
server/connection.js
Normal file
0
server/connection.js
Normal file
126
server/game.js
Normal file
126
server/game.js
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
// Node.
|
||||||
|
import {performance} from 'perf_hooks';
|
||||||
|
// 3rd party.
|
||||||
|
import immutablediff from 'immutablediff';
|
||||||
|
// 2nd party.
|
||||||
|
import {
|
||||||
|
create as createEntity,
|
||||||
|
EntityList,
|
||||||
|
registerTrait,
|
||||||
|
} from '@avocado/entity';
|
||||||
|
import {StateSynchronizer} from '@avocado/state';
|
||||||
|
// Traits.
|
||||||
|
import {Controllable} from './traits/controllable';
|
||||||
|
registerTrait(Controllable);
|
||||||
|
// Create game.
|
||||||
|
export default function(avocadoServer) {
|
||||||
|
avocadoServer.on('connect', createConnectionListener(avocadoServer));
|
||||||
|
setInterval(createMainLoop(avocadoServer), 1000 / 80);
|
||||||
|
}
|
||||||
|
// Entity tracking.
|
||||||
|
const entityList = new EntityList();
|
||||||
|
const stateSynchronizer = new StateSynchronizer({
|
||||||
|
entityList,
|
||||||
|
});
|
||||||
|
// Connection listener.
|
||||||
|
function createConnectionListener(avocadoServer) {
|
||||||
|
return (socket) => {
|
||||||
|
// Create and track a new entity for the connection.
|
||||||
|
const entity = createEntityForConnection();
|
||||||
|
entityList.addEntity(entity);
|
||||||
|
socket.entity = entity;
|
||||||
|
// Send complete state.
|
||||||
|
socket.send({
|
||||||
|
type: 'state-update',
|
||||||
|
payload: stateSynchronizer.state().toJS(),
|
||||||
|
});
|
||||||
|
// Listen for events.
|
||||||
|
socket.on('message', createMessageListener(avocadoServer, socket));
|
||||||
|
socket.on('disconnect', createDisconnectionListener(avocadoServer, socket));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle incoming messages.
|
||||||
|
function createMessageListener(avocadoServer, socket) {
|
||||||
|
const {entity} = socket;
|
||||||
|
return ({type, payload}) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'input':
|
||||||
|
entity.inputState = payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Handle disconnection.
|
||||||
|
function createDisconnectionListener(avocadoServer, socket) {
|
||||||
|
const {entity} = socket;
|
||||||
|
return () => {
|
||||||
|
entityList.removeEntity(entity);
|
||||||
|
avocadoServer.broadcast({
|
||||||
|
type: 'state-update',
|
||||||
|
payload: {
|
||||||
|
entityList: {
|
||||||
|
[entity.instanceUuid]: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Create an entity for a new connection.
|
||||||
|
function createEntityForConnection() {
|
||||||
|
const entity = createEntity();
|
||||||
|
return entity.fromJSON({
|
||||||
|
traits: {
|
||||||
|
animated: {
|
||||||
|
params: {
|
||||||
|
animations: {
|
||||||
|
idle: {
|
||||||
|
offset: [0, -48],
|
||||||
|
uri: '/idle.animation.json',
|
||||||
|
},
|
||||||
|
moving: {
|
||||||
|
offset: [0, -48],
|
||||||
|
uri: '/moving.animation.json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
controllable: {},
|
||||||
|
directional: {
|
||||||
|
params: {
|
||||||
|
directionCount: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
existent: {},
|
||||||
|
graphical: {},
|
||||||
|
mobile: {
|
||||||
|
state: {
|
||||||
|
speed: 400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
positioned: {
|
||||||
|
state: {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Main loop.
|
||||||
|
let lastTime = performance.now();
|
||||||
|
function createMainLoop(avocadoServer) {
|
||||||
|
return () => {
|
||||||
|
const now = performance.now();
|
||||||
|
const elapsed = (now - lastTime) / 1000;
|
||||||
|
lastTime = now;
|
||||||
|
entityList.tick(elapsed);
|
||||||
|
const diff = stateSynchronizer.diff();
|
||||||
|
if (StateSynchronizer.noChange === diff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
avocadoServer.broadcast({
|
||||||
|
type: 'state-update',
|
||||||
|
payload: diff,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
9
server/index.js
Normal file
9
server/index.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import http from 'http';
|
||||||
|
import {Server} from '@avocado/server/socket';
|
||||||
|
const httpServer = http.createServer();
|
||||||
|
// Listen.
|
||||||
|
httpServer.listen(8420, '0.0.0.0');
|
||||||
|
// Start game server.
|
||||||
|
import createGame from './game';
|
||||||
|
const avocadoServer = new Server(httpServer);
|
||||||
|
createGame(avocadoServer);
|
47
server/traits/controllable.js
Normal file
47
server/traits/controllable.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import * as I from 'immutable';
|
||||||
|
|
||||||
|
import {Trait} from '@avocado/entity';
|
||||||
|
|
||||||
|
// Input handling.
|
||||||
|
export class Controllable extends Trait {
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this._inputState = I.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners() {
|
||||||
|
return {
|
||||||
|
tick: (elapsed) => {
|
||||||
|
const {_inputState: inputState} = this;
|
||||||
|
if (0 === inputState.size) {
|
||||||
|
this.entity.currentAnimation = 'idle';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const movementVector = [0, 0];
|
||||||
|
if (inputState.has('MoveUp')) {
|
||||||
|
movementVector[1] -= 1;
|
||||||
|
}
|
||||||
|
if (inputState.has('MoveRight')) {
|
||||||
|
movementVector[0] += 1;
|
||||||
|
}
|
||||||
|
if (inputState.has('MoveDown')) {
|
||||||
|
movementVector[1] += 1;
|
||||||
|
}
|
||||||
|
if (inputState.has('MoveLeft')) {
|
||||||
|
movementVector[0] -= 1;
|
||||||
|
}
|
||||||
|
if (0 === movementVector[0] && 0 === movementVector[1]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.entity.requestMovement(movementVector);
|
||||||
|
this.entity.currentAnimation = 'moving';
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set inputState(inputState) {
|
||||||
|
this._inputState = I.Set(inputState);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
44
webpack.client.config.js
Normal file
44
webpack.client.config.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
|
const config = require('./webpack.common.config');
|
||||||
|
|
||||||
|
config.entry = {
|
||||||
|
client: [
|
||||||
|
'@babel/polyfill',
|
||||||
|
path.join(__dirname, 'client', 'index.js'),
|
||||||
|
path.join(__dirname, 'register-traits.js'),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
config.devServer = {
|
||||||
|
compress: true,
|
||||||
|
contentBase: path.resolve(__dirname, 'resource'),
|
||||||
|
disableHostCheck: true,
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
||||||
|
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
|
||||||
|
},
|
||||||
|
host: '0.0.0.0',
|
||||||
|
overlay: true,
|
||||||
|
port: 8421,
|
||||||
|
proxy: {
|
||||||
|
'/avocado': {
|
||||||
|
target: 'http://localhost:8420',
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stats: 'minimal',
|
||||||
|
watchContentBase: true,
|
||||||
|
};
|
||||||
|
config.devtool = 'eval-source-map';
|
||||||
|
config.node = {
|
||||||
|
fs: 'empty',
|
||||||
|
path: 'empty',
|
||||||
|
};
|
||||||
|
config.plugins.push(new HtmlWebpackPlugin({
|
||||||
|
template: path.resolve(__dirname, 'client', 'index.html'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
module.exports = config;
|
41
webpack.common.config.js
Normal file
41
webpack.common.config.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
mode: 'development',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: [
|
||||||
|
/(node_modules\/(?!@avocado))/,
|
||||||
|
],
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /register-traits.js/,
|
||||||
|
use: {
|
||||||
|
loader: './generate-traits',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: '[name].js',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
modules: [path.resolve(__dirname, 'node_modules')],
|
||||||
|
},
|
||||||
|
resolveLoader: {
|
||||||
|
modules: [path.resolve(__dirname, 'node_modules')],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
27
webpack.server.config.js
Normal file
27
webpack.server.config.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const nodeExternals = require('webpack-node-externals');
|
||||||
|
const StartServerPlugin = require('start-server-webpack-plugin');
|
||||||
|
|
||||||
|
const config = require('./webpack.common.config');
|
||||||
|
|
||||||
|
config.entry = {
|
||||||
|
server: [
|
||||||
|
'source-map-support/register',
|
||||||
|
'@babel/polyfill',
|
||||||
|
path.join(__dirname, 'server', 'index.js'),
|
||||||
|
path.join(__dirname, 'register-traits.js'),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
config.externals = [
|
||||||
|
nodeExternals({
|
||||||
|
whitelist: /@avocado/,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
config.plugins.push(new StartServerPlugin({
|
||||||
|
name: 'server.js',
|
||||||
|
restartable: false,
|
||||||
|
}));
|
||||||
|
config.target = 'node';
|
||||||
|
|
||||||
|
module.exports = config;
|
4733
yarn-error.log
Normal file
4733
yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user