mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-01-08 14:01:58 -05:00
AST create / change / move / delete from blockly
This commit is contained in:
parent
53779220b7
commit
981b85e78f
22 changed files with 11858 additions and 381 deletions
38
README.md
38
README.md
|
@ -32,6 +32,44 @@ make build
|
|||
</script>
|
||||
```
|
||||
|
||||
## Abstract Syntax Tree
|
||||
|
||||
#### Overview
|
||||
|
||||
#### Anatomy of a Block
|
||||
```json
|
||||
{
|
||||
"id": "^1r~63Gdl7;Dh?I*OP3_",
|
||||
"opcode": "wedo_motorclockwise",
|
||||
"next": null,
|
||||
"fields": {
|
||||
"DURATION": {
|
||||
"name": "DURATION",
|
||||
"value": null,
|
||||
"blocks": {
|
||||
"1?P=eV(OiDY3vMk!24Ip": {
|
||||
"id": "1?P=eV(OiDY3vMk!24Ip",
|
||||
"opcode": "math_number",
|
||||
"next": null,
|
||||
"fields": {
|
||||
"NUM": {
|
||||
"name": "NUM",
|
||||
"value": "10",
|
||||
"blocks": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"SUBSTACK": {
|
||||
"name": "SUBSTACK",
|
||||
"value": "@1ln(HsUO4!]*2*%BrE|",
|
||||
"blocks": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
```bash
|
||||
make test
|
||||
|
|
|
@ -13,10 +13,14 @@
|
|||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"htmlparser2": "3.9.0",
|
||||
"memoizee": "0.3.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"benchmark": "2.1.0",
|
||||
"eslint": "2.7.0",
|
||||
"json-loader": "0.5.4",
|
||||
"tap": "5.7.1",
|
||||
"webpack": "1.13.0"
|
||||
}
|
||||
|
|
87
src/engine/adapter.js
Normal file
87
src/engine/adapter.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
var html = require('htmlparser2');
|
||||
var memoize = require('memoizee');
|
||||
var parseDOM = memoize(html.parseDOM, {
|
||||
length: 1,
|
||||
resolvers: [String],
|
||||
max: 200
|
||||
});
|
||||
|
||||
/**
|
||||
* Adapter between block creation events and block representation which can be
|
||||
* used by the Scratch runtime.
|
||||
*
|
||||
* @param {Object} `Blockly.events.create`
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
module.exports = function (e) {
|
||||
// Validate input
|
||||
if (typeof e !== 'object') return;
|
||||
if (typeof e.blockId !== 'string') return;
|
||||
if (typeof e.xml !== 'object') return;
|
||||
|
||||
// Storage object
|
||||
var obj = {
|
||||
id: e.blockId,
|
||||
opcode: null,
|
||||
next: null,
|
||||
fields: {}
|
||||
};
|
||||
|
||||
// Set opcode
|
||||
if (typeof e.xml.attributes === 'object') {
|
||||
obj.opcode = e.xml.attributes.type.value;
|
||||
}
|
||||
|
||||
// Extract fields from event's `innerHTML`
|
||||
if (typeof e.xml.innerHTML !== 'string') return obj;
|
||||
if (e.xml.innerHTML === '') return obj;
|
||||
obj.fields = extract(parseDOM(e.xml.innerHTML));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts fields from a block's innerHTML.
|
||||
* @todo Extend this to support vertical grammar / nested blocks.
|
||||
*
|
||||
* @param {Object} DOM representation of block's innerHTML
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
function extract (dom) {
|
||||
// Storage object
|
||||
var fields = {};
|
||||
|
||||
// Field
|
||||
var field = dom[0];
|
||||
var fieldName = field.attribs.name;
|
||||
fields[fieldName] = {
|
||||
name: fieldName,
|
||||
value: null,
|
||||
blocks: {}
|
||||
};
|
||||
|
||||
// Shadow block
|
||||
var shadow = field.children[0];
|
||||
var shadowId = shadow.attribs.id;
|
||||
var shadowOpcode = shadow.attribs.type;
|
||||
fields[fieldName].blocks[shadowId] = {
|
||||
id: shadowId,
|
||||
opcode: shadowOpcode,
|
||||
next: null,
|
||||
fields: {}
|
||||
};
|
||||
|
||||
// Primitive
|
||||
var primitive = shadow.children[0];
|
||||
var primitiveName = primitive.attribs.name;
|
||||
var primitiveValue = primitive.children[0].data;
|
||||
fields[fieldName].blocks[shadowId].fields[primitiveName] = {
|
||||
name: primitiveName,
|
||||
value: primitiveValue,
|
||||
blocks: null
|
||||
};
|
||||
|
||||
return fields;
|
||||
}
|
|
@ -1,41 +1,5 @@
|
|||
function Primitives () {
|
||||
|
||||
// @todo
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -1,26 +1,19 @@
|
|||
var EventEmitter = require('events');
|
||||
var util = require('util');
|
||||
|
||||
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 () {
|
||||
// Bind event emitter
|
||||
EventEmitter.call(instance);
|
||||
|
||||
// Instantiate sequencer and primitives
|
||||
this.sequencer = new Sequencer(this);
|
||||
this.primitives = new Primitives();
|
||||
EventEmitter.call(this);
|
||||
|
||||
// State
|
||||
this.blocks = {};
|
||||
this.stacks = [];
|
||||
|
||||
window._BLOCKS = this.blocks;
|
||||
window._STACKS = this.stacks;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,19 +21,34 @@ function Runtime () {
|
|||
*/
|
||||
util.inherits(Runtime, EventEmitter);
|
||||
|
||||
Runtime.prototype.createBlock = function (e) {
|
||||
Runtime.prototype.createBlock = function (block) {
|
||||
// Create new block
|
||||
this.blocks[e.id] = {
|
||||
id: e.id,
|
||||
opcode: e.opcode,
|
||||
next: null,
|
||||
inputs: {}
|
||||
};
|
||||
this.blocks[block.id] = block;
|
||||
|
||||
// Walk each field and add any shadow blocks
|
||||
// @todo Expand this to cover vertical / nested blocks
|
||||
for (var i in block.fields) {
|
||||
var shadows = block.fields[i].blocks;
|
||||
for (var y in shadows) {
|
||||
var shadow = shadows[y];
|
||||
this.blocks[shadow.id] = shadow;
|
||||
};
|
||||
}
|
||||
|
||||
// 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);
|
||||
this.stacks.push(block.id);
|
||||
};
|
||||
|
||||
Runtime.prototype.changeBlock = function (args) {
|
||||
// Validate
|
||||
if (args.element !== 'field') return;
|
||||
if (typeof this.blocks[args.id] === 'undefined') return;
|
||||
if (typeof this.blocks[args.id].fields[args.name] === 'undefined') return;
|
||||
|
||||
// Update block value
|
||||
this.blocks[args.id].fields[args.name].value = args.value;
|
||||
};
|
||||
|
||||
Runtime.prototype.moveBlock = function (e) {
|
||||
|
@ -52,10 +60,14 @@ Runtime.prototype.moveBlock = function (e) {
|
|||
_this._deleteStack(e.id);
|
||||
|
||||
// Update new parent
|
||||
if (e.newInput === undefined) {
|
||||
if (e.newField === undefined) {
|
||||
_this.blocks[e.newParent].next = e.id;
|
||||
} else {
|
||||
_this.blocks[e.newParent].inputs[e.newInput] = e.id;
|
||||
_this.blocks[e.newParent].fields[e.newField] = {
|
||||
name: e.newField,
|
||||
value: e.id,
|
||||
blocks: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,30 +77,34 @@ Runtime.prototype.moveBlock = function (e) {
|
|||
_this.stacks.push(e.id);
|
||||
|
||||
// Update old parent
|
||||
if (e.oldInput === undefined) {
|
||||
if (e.oldField === undefined) {
|
||||
_this.blocks[e.oldParent].next = null;
|
||||
} else {
|
||||
delete _this.blocks[e.oldParent].inputs[e.oldInput];
|
||||
delete _this.blocks[e.oldParent].fields[e.oldField];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Runtime.prototype.changeBlock = function (e) {
|
||||
// @todo
|
||||
};
|
||||
|
||||
Runtime.prototype.deleteBlock = function (e) {
|
||||
// @todo Stop threads running on this stack
|
||||
|
||||
// Delete children
|
||||
// Get block
|
||||
var block = this.blocks[e.id];
|
||||
|
||||
// Delete children
|
||||
if (block.next !== null) {
|
||||
this.deleteBlock({id: block.next});
|
||||
}
|
||||
|
||||
// Delete inputs
|
||||
for (var i in block.inputs) {
|
||||
this.deleteBlock({id: block.inputs[i]});
|
||||
// Delete substacks and fields
|
||||
for (var field in block.fields) {
|
||||
if (field === 'SUBSTACK') {
|
||||
this.deleteBlock({id: block.fields[field].value});
|
||||
} else {
|
||||
for (var shadow in block.fields[field].blocks) {
|
||||
this.deleteBlock({id: shadow});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete stack
|
||||
|
@ -98,19 +114,6 @@ Runtime.prototype.deleteBlock = function (e) {
|
|||
delete this.blocks[e.id];
|
||||
};
|
||||
|
||||
Runtime.prototype.runAllStacks = function () {
|
||||
// @todo
|
||||
};
|
||||
|
||||
Runtime.prototype.runStack = function (e) {
|
||||
// @todo
|
||||
console.dir(e);
|
||||
};
|
||||
|
||||
Runtime.prototype.stopAllStacks = function () {
|
||||
// @todo
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
@ -126,7 +129,7 @@ Runtime.prototype._getNextBlock = function (id) {
|
|||
|
||||
Runtime.prototype._getSubstack = function (id) {
|
||||
if (typeof this.blocks[id] === 'undefined') return null;
|
||||
return this.blocks[id].inputs['SUBSTACK'];
|
||||
return this.blocks[id].fields['SUBSTACK'];
|
||||
};
|
||||
|
||||
module.exports = Runtime;
|
||||
|
|
|
@ -1,29 +1,5 @@
|
|||
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;
|
||||
function Sequencer () {
|
||||
// @todo
|
||||
}
|
||||
|
||||
Sequencer.prototype.stepAllThreads = function () {
|
||||
|
||||
};
|
||||
|
||||
Sequencer.prototype.stepThread = function (thread) {
|
||||
|
||||
};
|
||||
|
||||
Sequencer.prototype.startSubstack = function (thread) {
|
||||
|
||||
};
|
||||
|
||||
module.exports = Sequencer;
|
||||
|
|
|
@ -1,19 +1,5 @@
|
|||
/**
|
||||
* 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;
|
||||
function Thread () {
|
||||
// @todo
|
||||
}
|
||||
|
||||
module.exports = Thread;
|
||||
|
|
47
src/index.js
47
src/index.js
|
@ -2,6 +2,7 @@ var EventEmitter = require('events');
|
|||
var util = require('util');
|
||||
|
||||
var Runtime = require('./engine/runtime');
|
||||
var adapter = require('./engine/adapter');
|
||||
|
||||
/**
|
||||
* Handles connections between blocks, stage, and extensions.
|
||||
|
@ -30,23 +31,23 @@ function VirtualMachine () {
|
|||
// Blocks
|
||||
switch (e.type) {
|
||||
case 'create':
|
||||
instance.runtime.createBlock({
|
||||
instance.runtime.createBlock(adapter(e));
|
||||
break;
|
||||
case 'change':
|
||||
instance.runtime.changeBlock({
|
||||
id: e.blockId,
|
||||
opcode: e.xml.attributes.type.value
|
||||
element: e.element,
|
||||
name: e.name,
|
||||
value: e.newValue
|
||||
});
|
||||
break;
|
||||
case 'move':
|
||||
instance.runtime.moveBlock({
|
||||
id: e.blockId,
|
||||
oldParent: e.oldParentId,
|
||||
oldInput: e.oldInputName,
|
||||
oldField: e.oldInputName,
|
||||
newParent: e.newParentId,
|
||||
newInput: e.newInputName
|
||||
});
|
||||
break;
|
||||
case 'change':
|
||||
instance.runtime.changeBlock({
|
||||
id: e.blockId
|
||||
newField: e.newInputName
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
|
@ -55,19 +56,7 @@ function VirtualMachine () {
|
|||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// UI
|
||||
if (typeof e.element === 'undefined') return;
|
||||
switch (e.element) {
|
||||
case 'click':
|
||||
instance.runtime.runStack({
|
||||
id: e.blockId
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// @todo Forward runtime events
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,22 +64,6 @@ function VirtualMachine () {
|
|||
*/
|
||||
util.inherits(VirtualMachine, EventEmitter);
|
||||
|
||||
VirtualMachine.prototype.start = function () {
|
||||
// @todo Run all green flags
|
||||
};
|
||||
|
||||
VirtualMachine.prototype.stop = function () {
|
||||
// @todo Stop all threads
|
||||
};
|
||||
|
||||
VirtualMachine.prototype.save = function () {
|
||||
// @todo Serialize runtime state
|
||||
};
|
||||
|
||||
VirtualMachine.prototype.load = function () {
|
||||
// @todo Deserialize and apply runtime state
|
||||
};
|
||||
|
||||
/**
|
||||
* Export and bind to `window`
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/**
|
||||
* Constructor
|
||||
* @todo Swap out Date.now() with microtime module that works in node & browsers
|
||||
*/
|
||||
function Timer () {
|
||||
this.startTime = 0;
|
||||
|
|
0
test/benchmark/ast.js
Normal file
0
test/benchmark/ast.js
Normal file
26
test/fixtures/blocks.js
vendored
26
test/fixtures/blocks.js
vendored
|
@ -1,26 +0,0 @@
|
|||
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;
|
20
test/fixtures/events.json
vendored
Normal file
20
test/fixtures/events.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"create": {
|
||||
"blockId": "z!+#Nqr,_(V=xz0y7a@d",
|
||||
"workspaceId": "7Luws3lyb*Z98~Kk+IG|",
|
||||
"group": ";OswyM#@%`%,xOrhOXC=",
|
||||
"recordUndo": true,
|
||||
"xml": {
|
||||
"attributes": {
|
||||
"type": {
|
||||
"value": "wedo_motorclockwise"
|
||||
}
|
||||
},
|
||||
"innerHTML": "<value name=\"DURATION\"><shadow type=\"math_number\" id=\"!6Ahqg4f}Ljl}X5Hws?Z\"><field name=\"NUM\">10</field></shadow></value>"
|
||||
},
|
||||
"ids": [
|
||||
"z!+#Nqr,_(V=xz0y7a@d",
|
||||
"!6Ahqg4f}Ljl}X5Hws?Z"
|
||||
]
|
||||
}
|
||||
}
|
22
test/integration/index.js
Normal file
22
test/integration/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
var test = require('tap').test;
|
||||
var VirtualMachine = require('../../src/index');
|
||||
|
||||
test('spec', function (t) {
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('create', function (t) {
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('move', function (t) {
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('change', function (t) {
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('delete', function (t) {
|
||||
t.end();
|
||||
});
|
20
test/unit/adapter.js
Normal file
20
test/unit/adapter.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
var test = require('tap').test;
|
||||
var adapter = require('../../src/engine/adapter');
|
||||
var events = require('../fixtures/events.json');
|
||||
|
||||
test('spec', function (t) {
|
||||
t.type(adapter, 'function');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('create event', function (t) {
|
||||
var result = adapter(events.create);
|
||||
|
||||
t.type(result, 'object');
|
||||
t.type(result.id, 'string');
|
||||
t.type(result.opcode, 'string');
|
||||
t.type(result.fields, 'object');
|
||||
t.type(result.fields['DURATION'], 'object');
|
||||
|
||||
t.end();
|
||||
});
|
8
test/unit/primatives.js
Normal file
8
test/unit/primatives.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
var test = require('tap').test;
|
||||
var Primatives = require('../../src/engine/primatives');
|
||||
|
||||
test('spec', function (t) {
|
||||
t.type(Primatives, 'function');
|
||||
// @todo
|
||||
t.end();
|
||||
});
|
|
@ -24,7 +24,9 @@ test('create', function (t) {
|
|||
var r = new Runtime();
|
||||
r.createBlock({
|
||||
id: 'foo',
|
||||
opcode: 'TEST_BLOCK'
|
||||
opcode: 'TEST_BLOCK',
|
||||
next: null,
|
||||
fields: {}
|
||||
});
|
||||
|
||||
t.type(r.blocks['foo'], 'object');
|
||||
|
@ -37,11 +39,15 @@ test('move', function (t) {
|
|||
var r = new Runtime();
|
||||
r.createBlock({
|
||||
id: 'foo',
|
||||
opcode: 'TEST_BLOCK'
|
||||
opcode: 'TEST_BLOCK',
|
||||
next: null,
|
||||
fields: {}
|
||||
});
|
||||
r.createBlock({
|
||||
id: 'bar',
|
||||
opcode: 'TEST_BLOCK'
|
||||
opcode: 'TEST_BLOCK',
|
||||
next: null,
|
||||
fields: {}
|
||||
});
|
||||
|
||||
// Attach 'bar' to the end of 'foo'
|
||||
|
@ -69,7 +75,9 @@ test('delete', function (t) {
|
|||
var r = new Runtime();
|
||||
r.createBlock({
|
||||
id: 'foo',
|
||||
opcode: 'TEST_BLOCK'
|
||||
opcode: 'TEST_BLOCK',
|
||||
next: null,
|
||||
fields: {}
|
||||
});
|
||||
r.deleteBlock({
|
||||
id: 'foo'
|
||||
|
|
8
test/unit/sequencer.js
Normal file
8
test/unit/sequencer.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
var test = require('tap').test;
|
||||
var Sequencer = require('../../src/engine/sequencer');
|
||||
|
||||
test('spec', function (t) {
|
||||
t.type(Sequencer, 'function');
|
||||
// @todo
|
||||
t.end();
|
||||
});
|
|
@ -2,17 +2,10 @@ var test = require('tap').test;
|
|||
var VirtualMachine = require('../../src/index');
|
||||
|
||||
test('spec', function (t) {
|
||||
var vm = new VirtualMachine('foo');
|
||||
var vm = new VirtualMachine();
|
||||
|
||||
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();
|
||||
});
|
||||
|
|
|
@ -2,9 +2,7 @@ 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');
|
||||
// @todo
|
||||
t.end();
|
||||
});
|
||||
|
|
12
vm.min.js
vendored
12
vm.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -9,10 +9,21 @@ module.exports = {
|
|||
path: __dirname,
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.json$/,
|
||||
loader: 'json-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
include: /\.min\.js$/,
|
||||
minimize: true
|
||||
minimize: true,
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue