mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-22 22:12:28 -05:00
WIP
This commit is contained in:
parent
655556273a
commit
f9f47ed103
20 changed files with 2113 additions and 13 deletions
17
.eslintrc
Normal file
17
.eslintrc
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"rules": {
|
||||
"curly": [2, "multi-line"],
|
||||
"eol-last": [2],
|
||||
"indent": [2, 4],
|
||||
"quotes": [2, "single"],
|
||||
"linebreak-style": [2, "unix"],
|
||||
"max-len": [2, 80, 4],
|
||||
"semi": [2, "always"],
|
||||
"strict": [2, "never"]
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"browser": true
|
||||
},
|
||||
"extends": "eslint:recommended"
|
||||
}
|
5
.npmrc
5
.npmrc
|
@ -1,5 +0,0 @@
|
|||
engine-strict=true
|
||||
save-exact=true
|
||||
save-prefix=~
|
||||
init-license=BSD-3-Clause
|
||||
init-author-name=Massachusetts Institute of Technology
|
15
Makefile
15
Makefile
|
@ -1,12 +1,21 @@
|
|||
ESLINT=./node_modules/.bin/eslint
|
||||
NODE=node
|
||||
TAP=./node_modules/.bin/tap
|
||||
WEBPACK=./node_modules/.bin/webpack --progress --colors
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
build:
|
||||
$(WEBPACK)
|
||||
|
||||
watch:
|
||||
$(WEBPACK) --watch
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
lint:
|
||||
$(ESLINT) ./*.js
|
||||
$(ESLINT) ./lib/*.js
|
||||
$(ESLINT) ./src/*.js
|
||||
$(ESLINT) ./src/**/*.js
|
||||
$(ESLINT) ./test/**/*.js
|
||||
|
||||
test:
|
||||
|
@ -21,4 +30,4 @@ benchmark:
|
|||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
.PHONY: lint test coverage benchmark
|
||||
.PHONY: build lint test coverage benchmark
|
||||
|
|
22
README.md
22
README.md
|
@ -5,9 +5,29 @@
|
|||
npm install scratch-vm
|
||||
```
|
||||
|
||||
## Integration
|
||||
## Setup
|
||||
```js
|
||||
var VirtualMachine = require('scratch-vm');
|
||||
var vm = new VirtualMachine();
|
||||
|
||||
// Block events
|
||||
|
||||
// UI events
|
||||
|
||||
// Listen for events
|
||||
```
|
||||
|
||||
## Standalone Build
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
|
||||
```html
|
||||
<script src="/path/to/vm.js"></script>
|
||||
<script>
|
||||
var vm = new window.VirtualMachine();
|
||||
// do things
|
||||
</script>
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
|
0
index.js
0
index.js
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "scratch-vm",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"description": "Virtual Machine for Scratch 3.0",
|
||||
"author": "Massachusetts Institute of Technology",
|
||||
"license": "BSD-3-Clause",
|
||||
"homepage": "https://github.com/LLK/scratch-vm#readme",
|
||||
|
@ -9,13 +9,15 @@
|
|||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/LLK/scratch-vm.git"
|
||||
},
|
||||
"main": "index.js",
|
||||
"main": "./src/index.js",
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"benchmark": "2.1.0",
|
||||
"eslint": "2.7.0",
|
||||
"tap": "5.7.1"
|
||||
"tap": "5.7.1",
|
||||
"webpack": "1.13.0"
|
||||
}
|
||||
}
|
||||
|
|
41
src/engine/primatives.js
Normal file
41
src/engine/primatives.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
function Primitives () {
|
||||
|
||||
}
|
||||
|
||||
Primitives.prototype.event_whenflagclicked = function (thread, runtime) {
|
||||
// No-op: flags are started by the interpreter but don't do any action
|
||||
// Take 1/3 second to show running state
|
||||
if (Date.now() - thread.blockFirstTime < 300) {
|
||||
thread.yield = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
Primitives.prototype.control_repeat = function (thread, runtime) {
|
||||
// Take 1/3 second to show running state
|
||||
if (Date.now() - thread.blockFirstTime < 300) {
|
||||
thread.yield = true;
|
||||
return;
|
||||
}
|
||||
if (thread.repeatCounter == -1) {
|
||||
thread.repeatCounter = 10; // @todo from the arg
|
||||
}
|
||||
if (thread.repeatCounter > 0) {
|
||||
thread.repeatCounter -= 1;
|
||||
runtime.interpreter.startSubstack(thread);
|
||||
} else {
|
||||
thread.repeatCounter = -1;
|
||||
thread.nextBlock = runtime.getNextBlock(thread.blockPointer);
|
||||
}
|
||||
};
|
||||
|
||||
Primitives.prototype.control_forever = function (thread, runtime) {
|
||||
// Take 1/3 second to show running state
|
||||
if (Date.now() - thread.blockFirstTime < 300) {
|
||||
thread.yield = true;
|
||||
return;
|
||||
}
|
||||
runtime.interpreter.startSubstack(thread);
|
||||
};
|
||||
|
||||
module.exports = Primitives;
|
119
src/engine/runtime.js
Normal file
119
src/engine/runtime.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
var Primitives = require('./primatives');
|
||||
var Sequencer = require('./sequencer');
|
||||
var Thread = require('./thread');
|
||||
|
||||
var STEP_THREADS_INTERVAL = 1000 / 30;
|
||||
|
||||
/**
|
||||
* A simple runtime for blocks.
|
||||
*/
|
||||
function Runtime () {
|
||||
this.sequencer = new Sequencer(this);
|
||||
this.primitives = new Primitives();
|
||||
|
||||
// State
|
||||
this.blocks = {};
|
||||
this.stacks = [];
|
||||
}
|
||||
|
||||
Runtime.prototype.createBlock = function (e) {
|
||||
// Create new block
|
||||
this.blocks[e.id] = {
|
||||
id: e.id,
|
||||
opcode: e.opcode,
|
||||
next: null,
|
||||
inputs: {}
|
||||
};
|
||||
|
||||
// Push block id to stacks array. New blocks are always a stack even if only
|
||||
// momentary. If the new block is added to an existing stack this stack will
|
||||
// be removed by the `moveBlock` method below.
|
||||
this.stacks.push(e.id);
|
||||
};
|
||||
|
||||
Runtime.prototype.moveBlock = function (e) {
|
||||
var _this = this;
|
||||
|
||||
// Block has a new parent
|
||||
if (e.oldParent === undefined && e.newParent !== undefined) {
|
||||
// Remove stack
|
||||
_this._deleteStack(e.id);
|
||||
|
||||
// Update new parent
|
||||
if (e.newInput === undefined) {
|
||||
_this.blocks[e.newParent].next = e.id;
|
||||
} else {
|
||||
_this.blocks[e.newParent].inputs[e.newInput] = e.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Block was removed from parent
|
||||
if (e.newParentId === undefined && e.oldParent !== undefined) {
|
||||
// Add stack
|
||||
_this.stacks.push(e.id);
|
||||
|
||||
// Update old parent
|
||||
if (e.oldInput === undefined) {
|
||||
_this.blocks[e.oldParent].next = null;
|
||||
} else {
|
||||
delete _this.blocks[e.oldParent].inputs[e.oldInput];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Runtime.prototype.changeBlock = function (e) {
|
||||
// @todo
|
||||
};
|
||||
|
||||
Runtime.prototype.deleteBlock = function (e) {
|
||||
// @todo Stop threads running on this stack
|
||||
|
||||
// Delete children
|
||||
var block = this.blocks[e.id];
|
||||
if (block.next !== null) {
|
||||
this.deleteBlock({id: block.next});
|
||||
}
|
||||
|
||||
// Delete inputs
|
||||
for (var i in block.inputs) {
|
||||
this.deleteBlock({id: block.inputs[i]});
|
||||
}
|
||||
|
||||
// Delete stack
|
||||
this._deleteStack(e.id);
|
||||
|
||||
// Delete block
|
||||
delete this.blocks[e.id];
|
||||
};
|
||||
|
||||
Runtime.prototype.runAllStacks = function () {
|
||||
// @todo
|
||||
};
|
||||
|
||||
Runtime.prototype.runStack = function () {
|
||||
// @todo
|
||||
};
|
||||
|
||||
Runtime.prototype.stopAllStacks = function () {
|
||||
// @todo
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Runtime.prototype._deleteStack = function (id) {
|
||||
var i = this.stacks.indexOf(id);
|
||||
if (i > -1) this.stacks.splice(i, 1);
|
||||
};
|
||||
|
||||
Runtime.prototype._getNextBlock = function (id) {
|
||||
if (typeof this.blocks[id] === 'undefined') return null;
|
||||
return this.blocks[id].next;
|
||||
};
|
||||
|
||||
Runtime.prototype._getSubstack = function (id) {
|
||||
if (typeof this.blocks[id] === 'undefined') return null;
|
||||
return this.blocks[id].inputs['SUBSTACK'];
|
||||
};
|
||||
|
||||
module.exports = Runtime;
|
29
src/engine/sequencer.js
Normal file
29
src/engine/sequencer.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
var Timer = require('../util/timer');
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
function Sequencer (runtime) {
|
||||
// Bi-directional binding for runtime
|
||||
this.runtime = runtime;
|
||||
|
||||
// State
|
||||
this.runningThreads = [];
|
||||
this.workTime = 30;
|
||||
this.timer = new Timer();
|
||||
this.currentTime = 0;
|
||||
}
|
||||
|
||||
Sequencer.prototype.stepAllThreads = function () {
|
||||
|
||||
};
|
||||
|
||||
Sequencer.prototype.stepThread = function (thread) {
|
||||
|
||||
};
|
||||
|
||||
Sequencer.prototype.startSubstack = function (thread) {
|
||||
|
||||
};
|
||||
|
||||
module.exports = Sequencer;
|
19
src/engine/thread.js
Normal file
19
src/engine/thread.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Thread is an internal data structure used by the interpreter. It holds the
|
||||
* state of a thread so it can continue from where it left off, and it has
|
||||
* a stack to support nested control structures and procedure calls.
|
||||
*
|
||||
* @param {String} Unique block identifier
|
||||
*/
|
||||
function Thread (id) {
|
||||
this.topBlockId = id;
|
||||
this.blockPointer = id;
|
||||
this.blockFirstTime = -1;
|
||||
this.nextBlock = null;
|
||||
this.waiting = null;
|
||||
this.runningDeviceBlock = false;
|
||||
this.stack = [];
|
||||
this.repeatCounter = -1;
|
||||
}
|
||||
|
||||
module.exports = Thread;
|
148
src/index.js
Normal file
148
src/index.js
Normal file
|
@ -0,0 +1,148 @@
|
|||
var EventEmitter = require('events');
|
||||
var util = require('util');
|
||||
|
||||
var Runtime = require('./engine/runtime');
|
||||
|
||||
/**
|
||||
* Handles connections between blocks, stage, and extensions.
|
||||
*
|
||||
* @author Andrew Sliwinski <ascii@media.mit.edu>
|
||||
*/
|
||||
function VirtualMachine () {
|
||||
var instance = this;
|
||||
|
||||
// Bind event emitter and runtime to VM instance
|
||||
EventEmitter.call(instance);
|
||||
instance.runtime = new Runtime();
|
||||
|
||||
/**
|
||||
* Event listener for blockly. Handles validation and serves as a generic
|
||||
* adapter between the blocks and the runtime interface.
|
||||
*
|
||||
* @param {Object} Blockly "block" event
|
||||
*/
|
||||
instance.blockListener = function (e) {
|
||||
// Validate event
|
||||
if (typeof e !== 'object') return;
|
||||
if (typeof e.blockId !== 'string') return;
|
||||
|
||||
// Blocks
|
||||
switch (e.type) {
|
||||
case 'create':
|
||||
instance.runtime.createBlock({
|
||||
id: e.blockId,
|
||||
opcode: e.xml.attributes.type.value
|
||||
});
|
||||
break;
|
||||
case 'move':
|
||||
instance.runtime.moveBlock({
|
||||
id: e.blockId,
|
||||
oldParent: e.oldParentId,
|
||||
oldInput: e.oldInputName,
|
||||
newParent: e.newParentId,
|
||||
newInput: e.newInputName
|
||||
});
|
||||
break;
|
||||
case 'change':
|
||||
instance.runtime.changeBlock({
|
||||
id: e.blockId
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
instance.runtime.deleteBlock({
|
||||
id: e.blockId
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// @todo UI listener
|
||||
|
||||
// @todo Forward runtime events
|
||||
|
||||
// Event dispatcher
|
||||
// this.types = keymirror({
|
||||
// // Messages to runtime
|
||||
// CREATE_BLOCK: null,
|
||||
// MOVE_BLOCK: null,
|
||||
// CHANGE_BLOCK: null,
|
||||
// DELETE_BLOCK: null,
|
||||
//
|
||||
// ADD_DEVICE: null,
|
||||
// REMOVE_DEVICE: null,
|
||||
//
|
||||
// RUN_STRIP: null,
|
||||
// RUN_ALL_STRIPS: null,
|
||||
// STOP_ALL_STRIPS: null,
|
||||
// RUN_PALETTE_BLOCK: null,
|
||||
//
|
||||
// // Messages from runtime - subscribe to these
|
||||
// FEEDBACK_EXECUTING_BLOCK: null,
|
||||
// FEEDBACK_STOPPED_EXECUTING_BLOCK: null,
|
||||
// DEVICE_RUN_OP: null,
|
||||
// DEVICE_STOP_OP: null,
|
||||
//
|
||||
// // Tell back the interpreter device has finished an op
|
||||
// DEVICE_FINISHED_OP: null
|
||||
// });
|
||||
|
||||
// Bind block event stream
|
||||
// setTimeout(function () {
|
||||
// _this.emit('foo', 'bar');
|
||||
// }, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from EventEmitter
|
||||
*/
|
||||
util.inherits(VirtualMachine, EventEmitter);
|
||||
|
||||
// VirtualMachine.prototype.changeListener = function (e) {
|
||||
// var _this = this;
|
||||
// console.dir(this);
|
||||
//
|
||||
// switch (e.type) {
|
||||
// case 'create':
|
||||
// console.dir(e);
|
||||
// _this.runtime.createBlock(
|
||||
// e.blockId,
|
||||
// event.xml.attributes.type.value
|
||||
// );
|
||||
// break;
|
||||
// case 'change':
|
||||
// // @todo
|
||||
// break;
|
||||
// case 'move':
|
||||
// // @todo
|
||||
// break;
|
||||
// case 'delete':
|
||||
// // @todo
|
||||
// break;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// VirtualMachine.prototype.tapListener = function (e) {
|
||||
// // @todo
|
||||
// };
|
||||
|
||||
VirtualMachine.prototype.start = function () {
|
||||
this.runtime.runAllGreenFlags();
|
||||
};
|
||||
|
||||
VirtualMachine.prototype.stop = function () {
|
||||
this.runtime.stop();
|
||||
};
|
||||
|
||||
VirtualMachine.prototype.save = function () {
|
||||
// @todo Serialize runtime state
|
||||
};
|
||||
|
||||
VirtualMachine.prototype.load = function () {
|
||||
// @todo Deserialize and apply runtime state
|
||||
};
|
||||
|
||||
/**
|
||||
* Export and bind to `window`
|
||||
*/
|
||||
module.exports = VirtualMachine;
|
||||
if (typeof window !== 'undefined') window.VirtualMachine = module.exports;
|
21
src/util/timer.js
Normal file
21
src/util/timer.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Constructor
|
||||
* @todo Swap out Date.now() with microtime module that works in node & browsers
|
||||
*/
|
||||
function Timer () {
|
||||
this.startTime = 0;
|
||||
}
|
||||
|
||||
Timer.prototype.time = function () {
|
||||
return Date.now();
|
||||
};
|
||||
|
||||
Timer.prototype.start = function () {
|
||||
this.startTime = this.time();
|
||||
};
|
||||
|
||||
Timer.prototype.stop = function () {
|
||||
return this.startTime - this.time();
|
||||
};
|
||||
|
||||
module.exports = Timer;
|
26
test/fixtures/blocks.js
vendored
Normal file
26
test/fixtures/blocks.js
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
var events = require('events');
|
||||
var util = require('util');
|
||||
|
||||
/**
|
||||
* Simulates event emitter / listener patterns from Scratch Blocks.
|
||||
*
|
||||
* @author Andrew Sliwinski <ascii@media.mit.edu>
|
||||
*/
|
||||
function Blocks () {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from EventEmitter to enable messaging.
|
||||
*/
|
||||
util.inherits(VirtualMachine, events.EventEmitter);
|
||||
|
||||
Blocks.prototype.spaghetti = function () {
|
||||
this.emit('');
|
||||
};
|
||||
|
||||
Blocks.prototype.spam = function () {
|
||||
this.emit('');
|
||||
};
|
||||
|
||||
module.exports = Blocks;
|
81
test/unit/runtime.js
Normal file
81
test/unit/runtime.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
var test = require('tap').test;
|
||||
var Runtime = require('../../src/engine/runtime');
|
||||
|
||||
test('spec', function (t) {
|
||||
var r = new Runtime();
|
||||
|
||||
t.type(Runtime, 'function');
|
||||
t.type(r, 'object');
|
||||
t.ok(r instanceof Runtime);
|
||||
|
||||
t.type(r.blocks, 'object');
|
||||
t.type(r.stacks, 'object');
|
||||
t.ok(Array.isArray(r.stacks));
|
||||
|
||||
t.type(r.createBlock, 'function');
|
||||
t.type(r.moveBlock, 'function');
|
||||
t.type(r.changeBlock, 'function');
|
||||
t.type(r.deleteBlock, 'function');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('create', function (t) {
|
||||
var r = new Runtime();
|
||||
r.createBlock({
|
||||
id: 'foo',
|
||||
opcode: 'TEST_BLOCK'
|
||||
});
|
||||
|
||||
t.type(r.blocks['foo'], 'object');
|
||||
t.equal(r.blocks['foo'].opcode, 'TEST_BLOCK');
|
||||
t.notEqual(r.stacks.indexOf('foo'), -1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('move', function (t) {
|
||||
var r = new Runtime();
|
||||
r.createBlock({
|
||||
id: 'foo',
|
||||
opcode: 'TEST_BLOCK'
|
||||
});
|
||||
r.createBlock({
|
||||
id: 'bar',
|
||||
opcode: 'TEST_BLOCK'
|
||||
});
|
||||
|
||||
// Attach 'bar' to the end of 'foo'
|
||||
r.moveBlock({
|
||||
id: 'bar',
|
||||
newParent: 'foo'
|
||||
});
|
||||
t.equal(r.stacks.length, 1);
|
||||
t.equal(Object.keys(r.blocks).length, 2);
|
||||
t.equal(r.blocks['foo'].next, 'bar');
|
||||
|
||||
// Detach 'bar' from 'foo'
|
||||
r.moveBlock({
|
||||
id: 'bar',
|
||||
oldParent: 'foo'
|
||||
});
|
||||
t.equal(r.stacks.length, 2);
|
||||
t.equal(Object.keys(r.blocks).length, 2);
|
||||
t.equal(r.blocks['foo'].next, null);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('delete', function (t) {
|
||||
var r = new Runtime();
|
||||
r.createBlock({
|
||||
id: 'foo',
|
||||
opcode: 'TEST_BLOCK'
|
||||
});
|
||||
r.deleteBlock({
|
||||
id: 'foo'
|
||||
});
|
||||
|
||||
t.type(r.blocks['foo'], 'undefined');
|
||||
t.equal(r.stacks.indexOf('foo'), -1);
|
||||
t.end();
|
||||
});
|
|
@ -1,6 +1,18 @@
|
|||
var test = require('tap').test;
|
||||
var vm = require('../../index');
|
||||
var VirtualMachine = require('../../src/index');
|
||||
|
||||
test('spec', function (t) {
|
||||
var vm = new VirtualMachine('foo');
|
||||
|
||||
t.type(VirtualMachine, 'function');
|
||||
t.type(vm, 'object');
|
||||
|
||||
t.type(vm.blockListener, 'function');
|
||||
// t.type(vm.uiListener, 'function');
|
||||
// t.type(vm.start, 'function');
|
||||
// t.type(vm.stop, 'function');
|
||||
// t.type(vm.save, 'function');
|
||||
// t.type(vm.load, 'function');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
|
10
test/unit/thread.js
Normal file
10
test/unit/thread.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
var test = require('tap').test;
|
||||
var Thread = require('../../src/engine/thread');
|
||||
|
||||
test('spec', function (t) {
|
||||
var thread = new Thread('foo');
|
||||
|
||||
t.type(Thread, 'function');
|
||||
t.type(thread, 'object');
|
||||
t.end();
|
||||
});
|
41
test/unit/timer.js
Normal file
41
test/unit/timer.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
var test = require('tap').test;
|
||||
var Timer = require('../../src/util/timer');
|
||||
|
||||
test('spec', function (t) {
|
||||
var timer = new Timer();
|
||||
|
||||
t.type(Timer, 'function');
|
||||
t.type(timer, 'object');
|
||||
|
||||
t.type(timer.startTime, 'number');
|
||||
t.type(timer.time, 'function');
|
||||
t.type(timer.start, 'function');
|
||||
t.type(timer.stop, 'function');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('time', function (t) {
|
||||
var timer = new Timer();
|
||||
var time = timer.time();
|
||||
|
||||
t.ok(Date.now() >= time);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('start / stop', function (t) {
|
||||
var timer = new Timer();
|
||||
var start = timer.time();
|
||||
var delay = 100;
|
||||
var threshold = 1000 / 60; // 60 hz
|
||||
|
||||
// Start timer
|
||||
timer.start();
|
||||
|
||||
// Wait and stop timer
|
||||
setTimeout(function () {
|
||||
var stop = timer.stop();
|
||||
t.ok(stop >= -(delay + threshold) && stop <= -(delay - threshold));
|
||||
t.end();
|
||||
}, delay);
|
||||
});
|
1
vm.min.js
vendored
Normal file
1
vm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
webpack.config.js
Normal file
18
webpack.config.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
var webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
'vm': './src/index.js',
|
||||
'vm.min': './src/index.js'
|
||||
},
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: '[name].js'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
include: /\.min\.js$/,
|
||||
minimize: true
|
||||
})
|
||||
]
|
||||
};
|
Loading…
Reference in a new issue