diff --git a/README.md b/README.md index 710e55f..988c9a0 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,12 @@ __core__ is a concept introduced by Nicholas C. Zakas in this [video](https://ww It helps you create scalable applications written in Javascript, giving you some structure and patterns to keep everything separated. -## tl;dr - -* Check out [this video](https://www.youtube.com/watch?v=s9tdZSa74jo) - Introduction (portuguese) -* Check out [this video](https://www.youtube.com/watch?v=smYbfRrXSEs) - Isolation of DOM (portuguese) ## The Idea Conceptually, everything in your application is a module, and your modules should work independently from each other, so if one module breaks, the others should not. -A module should never talk directly to another module, for that you use a combination of listeners and notifications. +A module should never talks directly to another module, for that you use a combination of listeners and notifications. ## Getting Started @@ -35,246 +31,161 @@ Everything inside a red square is a module, they work in a way that they don't d Raw import -`import {Core} from "./node_modules/@eroc/core/dist/core.js";` +`import { Core } from "./node_modules/@eroc/core/dist/core.js";` With rollup, webpack or parcel -`import {Core} from "@eroc/core";` +`import { Core } from "@eroc/core";` NodeJs or Browserify -`const {Core} = require("@eroc/core");` +`const { Core } = require("@eroc/core");` Let's start with the tweet module. ### Building modules +A module exports start and optionally a stop function. + ```js -Core.register('tweet', function(sandbox) { - return { - init: function() { - console.log('starting tweet module'); - } - } -}); -``` +export { start, stop }; -This way you can register a new module, and the method `init` will be called once the module is started, if it exists. -```js -Core.start('tweet'); // Log: starting tweet module +const start = function (emitter) { + return {}; +}; + +const stop = function (instance) { + // instance is what start returned +}; ``` -For every module you have an enviroment called sandbox, this is the only guy your module will ever speak to. +To start this module in your main file: -It garantees that if your module tries to call something that doesn't exists or is broken, it won't break the module itself. +```js +import { Core } from "@eroc/core"; +import * as exampleModule from "./exampleModule.js"; + +const core = new Core(); +core.start(exampleModule); +``` -Now let's think about the tweet list, which fetches all tweets from your timeline. +Modules can only communicate via messages with other modules with the emitter received when start is called. It garantees that if a module tries to call something that doesn't exists or is broken, it won't break the module itself. ```js -Core.register('tweet-list', function(sandbox) { - return { - init: function() { +emitter.on(EVENT_NAME, function (data) { - } - } }); + +emitter.emit(EVENT_NAME, { a: 7 }); ``` -If you have multiple modules you can start everything using `Core.start();` with no parameters, and everything will be started. But if you need some specific order for starting your modules, you can call the modules you want first, and then use `Core.start()`. +To avoid spelling mistakes, import event names from a common file called eventNames.js. + ### Destroying modules You might want to stop a module in some point, this can be easily done using the method `Core.stop()`. ```js -Core.register('tweet-list', function(sandbox) { - return { - destroy: function() { - console.log('Module destroyed'); - } - } -}); - -Core.start('tweet-list'); -Core.stop('tweet-list'); // Log: Module destroyed +const exampleId = Core.start(exampleModule); +Core.stop(exampleId); ``` -When you stop a module, the method `destroy` will be called, if it exists. +When you stop a module, the function `stop` will be called, if it exists. ### Comunicating between modules -Now, thinking about Twitter, everytime you tweet something, it should appear on your tweet list right? but since our modules don't talk directly to each other, let's use `sandbox` to do this for us: +Now, thinking about Twitter, everytime you tweet something, it should appear on your tweet list right? but since our modules don't talk directly to each other, let's use the emitter. First of all, our `tweet` module should notify other modules that something has happened. ```js -Core.register('tweet', function(sandbox) { - return { - init: function(sandbox) { - // For the sake of simplicity, I'm gonna use a interval to submit tweets - setInterval(function() { - sandbox.notify({ - type: 'new-tweet', - data: { - author: 'Mauricio Soares', - text: 'core is pretty #cool' - } - }) - }, 5 * 1000) - } - } -}); +export { start }; + +const start = function(emitter) { + // For the sake of simplicity, use an interval + setInterval(function() { + emitter.emit(NEW_TWEET, { + author: 'Mauricio Soares', + text: 'core is pretty #cool' + }); + }, 5 * 1000) +}; ``` -Every 5 seconds, this module notifies everything that is listening to `new-tweet` that something has happened. If nothing is listening to it, than nothing happens. +Every 5 seconds, this module notifies everything that is listening to `NEW_TWEET` that something has happened. If nothing is listening to it, then nothing happens. Our `tweet-list` is going to listen for this notifications. ```js -Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen('new-tweet', this.newTweet); - }, - - newTweet: function(data) { - // does something with data - } - } -}); -``` - -Cool right? If one of those modules stop working, than it won't break the other one! +export { start }; -### Isolation of DOM - -If you have a DOM element with the same ID as the module name, it will be accessible inside the module using `this.el`. - -```html -
+const start = function (emitter) { + emitter.on(NEW_TWEET, (data) => { + // do something with the data + }); +}; ``` -```js -Core.register('tweet', function() { - return { - init: function() { - console.log(this.el); - } - } -}); - -Core.start('tweet'); // Log:
(DOM Reference) -``` - -If there's no DOM element, then `this.el` will return `null`. - -```js -// lets suppose we have jquery loaded before this - - -Core.register('tweet', function(sandbox) { - return { - init: function() { - jQuery('#tweet').on('click', this.newTweet); - }, - - newTweet: function() { - // handles click - } - }; -}); -``` - - - -A module should not talk to other modules directly anything else but the `sandbox`. +Cool right? If one of those modules stop working, than it won't break the other one! -### Last thoughts -This is basically how core works, below you will find the documentation of methods and parameters. ## Docs -#### Core.register( moduleName, constructor ) -Register a new module. +#### Core.start( module, options ) -- `moduleName` (string): The name of the module -- `constructor` (function): The implementation of the module -__Usage__ - -```js -Core.register('module', function() {}) -``` - -#### Core.start( moduleName ) -Starts the named module. If a value is returned in the `init` method, it can be grabbed in the return of the method `Core.start`. If no parameters are passed, it starts all unstarted modules. - -- `moduleName` (string): The name of the module +- `module` The module as a name-space (import * as exampleModule from "./exampleModule.js") +- `options` (function): The implementation of the module __Usage__ ```js -Core.start('module'); +Core.start(exampleModule) ``` -#### Core.stop( moduleName ) -Stops the named module. If a value is returned in the `destroy` method, it can be grabbed in the return of the method `Core.stop`, If no parameters are passed, it stop all modules. +#### Core.stop( moduleInstanceId ) -- `moduleName` (string): The name of the module +moduleInstanceId is what is returned by Core.start or the name used with Core.start __Usage__ ```js -Core.stop('module'); +Core.stop(moduleInstanceId); ``` -#### sandbox.listen( notification, callback, context, force ) -Listens to other modules notifications, to overwrite a notification you must use the parameter force - -- `notification` (string | array): The name of the notification you are listening to -- `callback` (function): The callback when the notification is triggered -- `context` (object): The value of `this` inside the callback -- `force` (boolean): If you want to overwrite a notification, use `true` here - -#### sandbox.notify( config ) -Notifies other modules - -- `config` (object): The configuration object - - `type` (string): The notification that will be triggered - - `data` (function | string | number | boolean | array): The data that will be passed in the callback +## tl;dr +* Check out [this video](https://www.youtube.com/watch?v=s9tdZSa74jo) - Introduction (portuguese) (Old API) -## Maintainer +## Maintainers -- Mauricio Soares - +- Mauricio Soares - https://github.com/mauriciosoares +- GrosSacASac ## Contributing -1. [Fork](http://help.github.com/forking/) core +1. [Fork](https://help.github.com/forking/) core 2. Create a topic branch - `git checkout -b my_branch` -3. Push to your branch - `git push origin my_branch` -4. Send me a [Pull Request](https://help.github.com/articles/using-pull-requests) -5. That's it! - -Please respect the indentation rules and code style. - -Use 2 spaces, not tabs. +3. Change some files and git commit +4. Push to your branch - `git push origin my_branch` +5. Send a [Pull Request](https://help.github.com/articles/using-pull-requests) -New features? Would you mind testing it? :) ## Testing -You need [NodeJS](http://nodejs.org/) installed on your machine +You need [NodeJS](https://nodejs.org/) installed on your machine 1. `npm i` 2. `npm run bundle` 3. `npm t` -## Release History +## Changelog +* 0.15.0 major architecture change * 2018-10-30 v0.13.0 remove deprecated startAll and stopAll * 2018-10-29 v0.12.0 Drop bower and publish on npm * 2018 Various changes diff --git a/examples/advanced/index.html b/examples/advanced/index.html deleted file mode 100644 index 5212cf4..0000000 --- a/examples/advanced/index.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Document - - -
-
- -
-
- - - - - - - diff --git a/examples/advanced/main.js b/examples/advanced/main.js deleted file mode 100644 index f2aea32..0000000 --- a/examples/advanced/main.js +++ /dev/null @@ -1,10 +0,0 @@ -import {Core} from "../../src/core/core.js"; -import "./tweet-form.js"; -import "./tweet-list.js"; -import "./tweet-counter.js"; - - -Core.start('tweet-counter', 'first counter'); -Core.start('tweet-form'); -Core.start('tweet-list'); -Core.start('tweet-counter', 'second counter'); diff --git a/examples/advanced/projectCommon.js b/examples/advanced/projectCommon.js deleted file mode 100644 index a50fb78..0000000 --- a/examples/advanced/projectCommon.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -this file exports the common interface for the entire project -common things: ui, network, event symbols, etc ... -*/ - -export {$, fetcher, events, errors} - -const $ = window.$; - -// like fetch but throws on 400+ -const fetcher = (url, options) => { - return fetch(url, options).then((response) => { - if (!response.ok) { - throw response; - } - return response; - }); -}; - -const events = { - receiveTweet: Symbol(), - submitTweet: Symbol() -}; - -const errors = { - tweetTooLong: Symbol() -}; diff --git a/examples/advanced/tweet-counter.js b/examples/advanced/tweet-counter.js deleted file mode 100644 index fb9190c..0000000 --- a/examples/advanced/tweet-counter.js +++ /dev/null @@ -1,31 +0,0 @@ -import {Core} from "../../src/core/core.js"; -import {$, fetcher, events} from "./projectCommon.js"; - -Core.register('tweet-counter', function(sandbox) { - return { - init: function() { - this.counter = document.createElement("output") - this.count = 0; - this.updateCount(); - document.body.appendChild(this.counter); - this.listen(); - }, - - listen: function() { - sandbox.listen(events.receiveTweet, this.newTweet, this); - }, - - updateCount: function() { - this.counter.textContent = this.count; - }, - - newTweet: function(data) { - this.count++; - this.updateCount(); - }, - - destroy: function() { - this.counter.remove(); - } - } -}, true); diff --git a/examples/advanced/tweet-form.js b/examples/advanced/tweet-form.js deleted file mode 100644 index 3e294f0..0000000 --- a/examples/advanced/tweet-form.js +++ /dev/null @@ -1,37 +0,0 @@ -import {Core} from "../../src/core/core.js"; -import {$, fetcher, events} from "./projectCommon.js"; - - -Core.register('tweet-form', function(sandbox) { - return { - init: function() { - this.$form = $('#tweet-form'); - this.$input = this.$form.find('input'); - - this.addListeners(); - }, - - addListeners: function() { - this.$form.on('submit', this.onSubmit.bind(this)); - }, - - onSubmit: function(e) { - e.preventDefault(); - - var newTweet = this.$input[0].value; - this.$input[0].value = ''; - - this.notify(events.submitTweet); - }, - - notify: function(tweet) { - sandbox.notify({ - type: 'new-tweet', - data: { - tweet: tweet, - author: '@omauriciosoares' - } - }); - } - } -}); diff --git a/examples/advanced/tweet-list.js b/examples/advanced/tweet-list.js deleted file mode 100644 index 4340523..0000000 --- a/examples/advanced/tweet-list.js +++ /dev/null @@ -1,34 +0,0 @@ -import {Core} from "../../src/core/core.js"; -import {$, fetcher, events} from "./projectCommon.js"; - - -Core.register('tweet-list', function(sandbox) { - return { - init: function() { - this.$list = $('#tweet-list'); - - this.listen(); - }, - - listen: function() { - sandbox.listen(events.receiveTweet, this.newTweet, this); - }, - - newTweet: function(data) { - var newTweetHtml = this.getHtml(data); - - this.$list.prepend(newTweetHtml); - }, - - getHtml: function(data) { - var li = $( - `
  • - ${data.author}
    - ${data.tweet} -
  • `); - li.append(); - - return li; - } - } -}); diff --git a/examples/advanced/tweet-validator.js b/examples/advanced/tweet-validator.js deleted file mode 100644 index aa671c6..0000000 --- a/examples/advanced/tweet-validator.js +++ /dev/null @@ -1,26 +0,0 @@ -import {Core} from "../../src/core/core.js"; -import {$, fetcher, events, errors} from "./projectCommon.js"; - - -Core.register('tweet-list', function(sandbox) { - return { - init: function() { - this.maxLength = 100; - - this.listen(); - }, - - listen: function() { - sandbox.listen(events.submitTweet, this.validateTweet, this); - }, - - validateTweet: function(data) { - if (data.length > this.maxLength) { - this.notify(events.error, errors.tweetTooLong); - return; - } - this.notify(events.receiveTweet, data); - } - - } -}); diff --git a/examples/simple/configuration.js b/examples/simple/configuration.js new file mode 100644 index 0000000..8552811 --- /dev/null +++ b/examples/simple/configuration.js @@ -0,0 +1,9 @@ +/* this file exports common configuration for the project */ +export { + configuration, +}; + + +const configuration = { + +}; diff --git a/examples/simple/dependencies.js b/examples/simple/dependencies.js new file mode 100644 index 0000000..7a27e98 --- /dev/null +++ b/examples/simple/dependencies.js @@ -0,0 +1,18 @@ +/* this file imports the common dependencies for the project and re exports as a common interface +common things: ui, network, etc ... */ + +export { fetcher, /* x, y, */ }; +// import { x } from "z"; +// import { y } from "w"; + +// like fetch but throws on 400+ +const fetcher = (url, options) => { + return fetch(url, options).then((response) => { + if (!response.ok) { + throw response; + } + return response; + }); +}; + + diff --git a/examples/simple/eventNames.js b/examples/simple/eventNames.js new file mode 100644 index 0000000..756ccaf --- /dev/null +++ b/examples/simple/eventNames.js @@ -0,0 +1,6 @@ +export { + NEW_TWEET, +}; + + +const NEW_TWEET = `NEW_TWEET`; diff --git a/examples/simple/index.html b/examples/simple/index.html index 5212cf4..306a0e3 100644 --- a/examples/simple/index.html +++ b/examples/simple/index.html @@ -7,7 +7,7 @@
    - +
    @@ -18,9 +18,6 @@ - diff --git a/examples/simple/main.js b/examples/simple/main.js index f2aea32..e64c8ef 100644 --- a/examples/simple/main.js +++ b/examples/simple/main.js @@ -1,10 +1,32 @@ -import {Core} from "../../src/core/core.js"; -import "./tweet-form.js"; -import "./tweet-list.js"; -import "./tweet-counter.js"; +import { Core, ALL } from "../../src/core/core.js"; +// import { } from "./eventNames.js"; +// import { x, y } from "./dependencies.js"; +// import { configuration } from "./configuration.js"; +import * as tweetForm from "./tweet-form.js"; +import * as tweetList from "./tweet-list.js"; +import * as tweetCounter from "./tweet-counter.js"; + + +const core = new Core(); +core.start(tweetForm); +core.start(tweetList); +core.start(tweetCounter, { name: 'first counter' }); +core.start(tweetCounter, { name: 'second counter' }); + + +// extras + + +// stop a module +setTimeout(() => { + console.info('stopping the second tweet counter'); + core.stop('second counter'); +}, 10 * 1000); + + +// listen for all events +core.on(ALL, ({ name, data }) => { + console.debug(`event ${String(name)} with data`, data); +}); -Core.start('tweet-counter', 'first counter'); -Core.start('tweet-form'); -Core.start('tweet-list'); -Core.start('tweet-counter', 'second counter'); diff --git a/examples/simple/tweet-counter.js b/examples/simple/tweet-counter.js index 533d3c0..8eb4691 100644 --- a/examples/simple/tweet-counter.js +++ b/examples/simple/tweet-counter.js @@ -1,30 +1,31 @@ -import {Core} from "../../src/core/core.js"; +export { start, stop }; +import { NEW_TWEET } from "./eventNames.js"; +// import { x, y } from "./dependencies.js"; +// import { configuration } from "./configuration.js"; -Core.register('tweet-counter', function(sandbox) { - return { - init: function() { - this.counter = document.createElement("output") - this.count = 0; - this.updateCount(); - document.body.appendChild(this.counter); - this.listen(); - }, +const initialCount = 0; - listen: function() { - sandbox.listen('new-tweet', this.newTweet, this); - }, +const start = function (emitter) { + const counter = document.createElement(`output`); + const instance = { + counter, + count: initialCount, + }; + updateCount(instance); + document.body.appendChild(counter); + emitter.on(NEW_TWEET, newTweet.bind(undefined, instance)); + return instance; +}; - updateCount: function() { - this.counter.textContent = this.count; - }, +const stop = function (instance) { + instance.counter.remove(); +}; - newTweet: function(data) { - this.count++; - this.updateCount(); - }, +const updateCount = function (instance) { + instance.counter.textContent = instance.count; +}; - destroy: function() { - this.counter.remove(); - } - } -}, true); +const newTweet = function (instance) { + instance.count += 1; + updateCount(instance); +}; diff --git a/examples/simple/tweet-form.js b/examples/simple/tweet-form.js index a8b8e0e..14bf319 100644 --- a/examples/simple/tweet-form.js +++ b/examples/simple/tweet-form.js @@ -1,36 +1,38 @@ -import {Core} from "../../src/core/core.js"; - - -Core.register('tweet-form', function(sandbox) { - return { - init: function() { - this.$form = $('#tweet-form'); - this.$input = this.$form.find('input'); - - this.addListeners(); - }, - - addListeners: function() { - this.$form.on('submit', this.onSubmit.bind(this)); - }, - - onSubmit: function(e) { - e.preventDefault(); - - var newTweet = this.$input[0].value; - this.$input[0].value = ''; - - this.notify(newTweet); - }, - - notify: function(tweet) { - sandbox.notify({ - type: 'new-tweet', - data: { - tweet: tweet, - author: '@omauriciosoares' - } - }); - } - } -}); +export { start, stop }; +import { NEW_TWEET } from "./eventNames.js"; +// import { x, y } from "./dependencies.js"; +// import { configuration } from "./configuration.js"; + + +const start = function (emitter) { + const form = document.getElementById(`tweet-form`); + const input = form[`input`]; + + const instance = { + form, + input, + onSubmit: undefined, + emitter, + }; + instance.onSubmit = submit.bind(undefined, instance); + + form.addEventListener(`submit`, instance.onSubmit, false); + + return instance; +}; + +const stop = function (instance) { + instance.form.removeEventListener(`submit`, instance.onSubmit, false); +}; + +const submit = function (instance, event) { + event.preventDefault(); + + const newTweet = instance.input.value; + instance.input.value = ``; + + instance.emitter.emit(NEW_TWEET, { + tweet: newTweet, + author: `@omauriciosoares` + }); +}; diff --git a/examples/simple/tweet-list.js b/examples/simple/tweet-list.js index 6d61a82..d8972ae 100644 --- a/examples/simple/tweet-list.js +++ b/examples/simple/tweet-list.js @@ -1,33 +1,30 @@ -import {Core} from "../../src/core/core.js"; - - -Core.register('tweet-list', function(sandbox) { - return { - init: function() { - this.$list = $('#tweet-list'); - - this.listen(); - }, - - listen: function() { - sandbox.listen('new-tweet', this.newTweet, this); - }, - - newTweet: function(data) { - var newTweetHtml = this.getHtml(data); - - this.$list.prepend(newTweetHtml); - }, - - getHtml: function(data) { - var li = $( - `
  • - ${data.author}
    - ${data.tweet} -
  • `); - li.append(); - - return li; - } - } -}); +export { start, stop }; +import { NEW_TWEET } from "./eventNames.js"; +// import { x, y } from "./dependencies.js"; +// import { configuration } from "./configuration.js"; + + +const start = function (emitter) { + const instance = { + list: document.getElementById(`tweet-list`), + }; + + emitter.on(NEW_TWEET, newTweet.bind(undefined, instance)); + return instance; +}; + +const stop = function (instance) { + instance.list.innerHTML = ``; +}; + +const newTweet = function (instance, data) { + const tweetElement = createElementWithTweet(data); + instance.list.prepend(tweetElement); +}; + +const createElementWithTweet = function (data) { + const li = document.createElement(`li`); + li.className = `tweetlist-item`; + li.innerHTML = `${data.author}
    ${data.tweet}`; + return li; +}; diff --git a/package-lock.json b/package-lock.json index 84fee8e..52cbf8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@eroc/core", - "version": "0.13.5", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -11,9 +11,9 @@ "dev": true }, "@types/node": { - "version": "10.12.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.19.tgz", - "integrity": "sha512-2NVovndCjJQj6fUUn9jCgpP4WSqr+u1SoUZMZyJkhGeBFsm6dE46l31S7lPUYt9uQ28XI+ibrJA1f5XyH5HNtA==", + "version": "12.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz", + "integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==", "dev": true }, "@zeit/schemas": { @@ -23,19 +23,19 @@ "dev": true }, "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "acorn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.6.tgz", - "integrity": "sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", "dev": true }, "ajv": { @@ -189,12 +189,12 @@ "dev": true }, "compressible": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", - "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", + "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", "dev": true, "requires": { - "mime-db": ">= 1.36.0 < 2" + "mime-db": ">= 1.40.0 < 2" } }, "compression": { @@ -256,6 +256,11 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "event-e3": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/event-e3/-/event-e3-8.0.2.tgz", + "integrity": "sha512-yKePQrhE1quG/EkdLC4XxuKpPYnvpGwOMLWPCM4bwzkwutgscc4c/uRAKjErKICJqhVWFwaAVn43V4H1c+4doQ==" + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -313,9 +318,9 @@ "dev": true }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -343,9 +348,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { @@ -373,19 +378,19 @@ "dev": true }, "jasmine": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.3.1.tgz", - "integrity": "sha512-/vU3/H7U56XsxIXHwgEuWpCgQ0bRi2iiZeUpx7Nqo8n1TpoDHfZhkPIc7CO8I4pnMzYsi3XaSZEiy8cnTfujng==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.4.0.tgz", + "integrity": "sha512-sR9b4n+fnBFDEd7VS2el2DeHgKcPiMVn44rtKFumq9q7P/t8WrxsVIZPob4UDdgcDNCwyDqwxCt4k9TDRmjPoQ==", "dev": true, "requires": { - "glob": "^7.0.6", - "jasmine-core": "~3.3.0" + "glob": "^7.1.3", + "jasmine-core": "~3.4.0" } }, "jasmine-core": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.3.0.tgz", - "integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.4.0.tgz", + "integrity": "sha512-HU/YxV4i6GcmiH4duATwAbJQMlE0MsDIR5XmSVxURxKHn3aGAdbY1/ZJFmVRbKtnLwIxxMJD7gYaPsypcbYimg==", "dev": true }, "json-schema-traverse": { @@ -405,18 +410,18 @@ } }, "mime-db": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true }, "mime-types": { - "version": "2.1.21", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, "requires": { - "mime-db": "~1.37.0" + "mime-db": "1.40.0" } }, "minimatch": { @@ -441,9 +446,9 @@ "dev": true }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "npm-run-path": { @@ -456,9 +461,9 @@ } }, "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true }, "once": { @@ -550,14 +555,14 @@ } }, "rollup": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.1.2.tgz", - "integrity": "sha512-OkdMxqMl8pWoQc5D8y1cIinYQPPLV8ZkfLgCzL6SytXeNA2P7UHynEQXI9tYxuAjAMsSyvRaWnyJDLHMxq0XAg==", + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.19.4.tgz", + "integrity": "sha512-G24w409GNj7i/Yam2cQla6qV2k6Nug8bD2DZg9v63QX/cH/dEdbNJg8H4lUm5M1bRpPKRUC465Rm9H51JTKOfQ==", "dev": true, "requires": { "@types/estree": "0.0.39", - "@types/node": "*", - "acorn": "^6.0.5" + "@types/node": "^12.6.9", + "acorn": "^6.2.1" } }, "safe-buffer": { @@ -567,9 +572,9 @@ "dev": true }, "serve": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/serve/-/serve-10.1.1.tgz", - "integrity": "sha512-B1ca73zGFRS/bYQkbDw6BVEpRiUKdtnkwtvkMjx598jU5tyieua9lHyqdwUoup4/ek20I74EzncTC0gZuYng4Q==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/serve/-/serve-11.1.0.tgz", + "integrity": "sha512-+4wpDtOSS+4ZLyDWMxThutA3iOTawX2+yDovOI8cjOUOmemyvNlHyFAsezBlSgbZKTYChI3tzA1Mh0z6XZ62qA==", "dev": true, "requires": { "@zeit/schemas": "2.6.0", @@ -579,14 +584,14 @@ "chalk": "2.4.1", "clipboardy": "1.2.3", "compression": "1.7.3", - "serve-handler": "5.0.7", + "serve-handler": "6.1.0", "update-check": "1.5.2" } }, "serve-handler": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-5.0.7.tgz", - "integrity": "sha512-PuLoJHAO2jj3p1fYWfXVHsEqNesx1+h+6qj0FIWrCe526ZtpDqeYuKA4knE5pjK9xoOVShoB+qGOP93EY46xEw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.0.tgz", + "integrity": "sha512-63N075Tn3PsFYcu0NVV7tb367UbiW3gnC+/50ohL4oqOhAG6bmbaWqiRcXQgbzqc0ALBjSAzg7VTfa0Qw4E3hA==", "dev": true, "requires": { "bytes": "3.0.0", diff --git a/package.json b/package.json index 6fb4064..63e693e 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,16 @@ { "name": "@eroc/core", - "version": "0.14.0", + "version": "1.0.0", "main": "dist/core.umd.js", - "module": "dist/core.js", + "module": "dist/core.es.js", "description": "Lightweight framework for scalable applications", "author": "Mauricio Soares", "homepage": "https://github.com/msodeveloper/core.js", "license": "MIT", "scripts": { - "serve": "serve", + "serve": "serve . -p 8080", "bundle": "rollup --config tools/rollup.config.js", + "bundle-watch": "rollup --config tools/rollup.config.js --watch", "prepublishOnly": "npm run bundle", "test": "jasmine --config=tests/jasmine.json" }, @@ -27,14 +28,18 @@ "url": "https://github.com/msodeveloper/core.js/issues" }, "keywords": [ - "Framework", "core.js", + "core", + "framework", "lightweight", "scalable" ], "devDependencies": { - "jasmine": "^3.3.1", - "rollup": "^1.1.2", - "serve": "^10.1.1" + "jasmine": "^3.4.0", + "rollup": "^1.19.4", + "serve": "^11.1.0" + }, + "dependencies": { + "event-e3": "^8.0.2" } } diff --git a/src/core.js b/src/core.js new file mode 100644 index 0000000..ba37e6d --- /dev/null +++ b/src/core.js @@ -0,0 +1,60 @@ +export { Core, ALL }; +import EventEmitter from "../node_modules/event-e3/event-e3.js"; + + +const ALL = Symbol(); + +const Core = class { + constructor() { + this.moduleInstances = new Map(); + EventEmitter(this); + } + + register() { + + }; + + start(module, { name = Symbol() } = {}) { + if (this.moduleInstances.has(name)) { + throw `module with name ${name} already started`; + } + + const emitter = new EventEmitter(); + + // emulate emitter.on(ANY, (name, data) => { + emitter.emit = (name, data) => { + this.emit(name, data); + this.emit(ALL, { name, data }); + this.moduleInstances.forEach(({ emitter }) => { + EventEmitter.prototype.emit.call(emitter, name, data); + }); + }; + + this.moduleInstances.set(name, { + module, + instance: module.start(emitter), + name, + emitter, + }); + + return name; + } + + stop(name) { + const wrapper = this.moduleInstances.get(name); + + if (!wrapper) { + //err('!stop', module); + return false; + } + + wrapper.emitter.off(); + if (wrapper.module.stop) { + wrapper.module.stop(wrapper.instance); + } + + this.moduleInstances.delete(name); + + return true; + } +}; diff --git a/src/core/core.js b/src/core/core.js deleted file mode 100644 index 3bdd203..0000000 --- a/src/core/core.js +++ /dev/null @@ -1,110 +0,0 @@ -export {Core, CoreClass}; -import {err} from "../helpers/err.js"; -import {Sandbox} from "../sandbox/sandbox.js"; - -/** -* The constructor of Core -* -* @class CoreClass -* @constructor -*/ -const CoreClass = class { - constructor() { - this.modules = {}; - this.moduleInstances = {}; - } - - /** -* Registers a new module -* -* @method register -* @param {string} module the name of the new module -* @param {function} constructor the constructor of the new module -*/ -register (module, constructor, factory = false) { - if (this.modules[module]) { - err('!!module', module); - return false; - } - this.modules[module] = { - constructor, - factory - }; -}; - -/** -* Starts a registered module, if no module is passed, it starts all modules -* -* @method start -* @param {string} moduleName -* @param {string|undefined} alias -*/ -start(moduleName, alias = moduleName) { - if (!moduleName) { - Object.keys(this.modules).forEach(moduleName => { - if (!this.moduleInstances[moduleName]) { - this.start(moduleName); - } - }); - return; - } - - const moduleWrapper = this.modules[moduleName]; - - if (!moduleWrapper) { - console.error(`Could not start ${moduleName}, it must be registered first`); - return false; - } - - if (this.moduleInstances[alias] && !moduleWrapper.factory) { - err('!start', moduleName); - return false; - } - - const instance = new moduleWrapper.constructor(new Sandbox(alias)); - this.moduleInstances[alias] = instance; - - if (instance.init) { - return instance.init(); - } - return true; -} - -/** -* Stops a registered module -* -* @method start -* @param {string} module the name of the module -*/ -stop (moduleName) { - if (!moduleName) { - Object.keys(this.moduleInstances).forEach(alias => { - this.stop(alias); - }); - return; - } - - const instance = this.moduleInstances[moduleName]; - - - if (!instance) { - //err('!stop', module); - return false; - } - - let stopReturn; - if (instance.destroy) { - stopReturn = instance.destroy(); - } - - delete this.moduleInstances[moduleName]; - - Sandbox.clearNotifications(moduleName); - - return stopReturn; -} - -} - - -const Core = new CoreClass(); diff --git a/src/helpers/err.js b/src/helpers/err.js deleted file mode 100644 index 9597e84..0000000 --- a/src/helpers/err.js +++ /dev/null @@ -1,19 +0,0 @@ -export {err} - -/** -* Handles error messages -* -* @method err -* @param {string} error the type of the error -* @param {function} message the complementary message to the error -*/ -const err = function (error, message) { - console.error(`${messages[error]}: "${message}"`); -}; - -const messages = { - '!start': `Could not start the given module, it's either already started and is not facory`, - '!stop': `Could not stop the given module, it's either already stopped or is not registered`, - '!!module': `Can't register an already registered module`, - '!!listen': `There's already an listen handler to the notification` -}; diff --git a/src/sandbox/sandbox.js b/src/sandbox/sandbox.js deleted file mode 100644 index 8def8e3..0000000 --- a/src/sandbox/sandbox.js +++ /dev/null @@ -1,96 +0,0 @@ -export {Sandbox}; -import {err} from "../helpers/err.js"; - - -/** -* All notifications from sandbox -* -* @private -*/ -const notifications = {}; - -/** -* The constructor of Sandbox -* -* @class Sandbox -* @constructor -*/ -const Sandbox = class { - constructor(module) { - this.module = module; - } - - -/** -* Notifies other modules from an specific notification -* -* @method notify -* @param {object} notification the object with notifications configs -*/ -notify (notification) { - Object.values(notifications).forEach(listener => { - const listening = listener[notification.type]; - if (listening) { - listening.callback.call(listening.context, notification.data); - } - }); -} - -/** -* Makes a module listen to an specific notification -* -* @method listen -* @param {string | array} notification the notification that the module will be listening to -*/ -listen(notification) { - if (!Array.isArray(notification)) { - return this.addNotification.apply(this, arguments); - } - const args = Array.from(arguments); - - notification.forEach(aNotification => { - args[0] = aNotification; - this.addNotification.apply(this, args); - }); -} - -/** -* Adds the module listener to the notifications configuration -* -* @method addNotification -* @param {string} notification the name of the notification -* @param {function} callback the callback that will be triggered when the notification is called -* @param {object} context the value of "this" -* @param {boolean} replace if the notification already exists, it forces to rewrite it -*/ -addNotification(notification, callback, context, replace) { - let addNotification = false; - - if (!notifications[this.module] || !notifications[this.module][notification]) { - addNotification = true; - } else if (replace) { - addNotification = true; - } else { - err('!!listen', notification); - } - - if (addNotification) { - notifications[this.module] = notifications[this.module] || {}; - notifications[this.module][notification] = { - callback: callback, - context: context || undefined - }; - } -} - -}; - - /** -* Clear all notifications from an specific module -* -* @method clearNotifications -* @param {string} module the name of the module -*/ -Sandbox.clearNotifications = function(module) { - delete notifications[module]; -}; diff --git a/tests/core.js b/tests/core.js new file mode 100644 index 0000000..80f30c4 --- /dev/null +++ b/tests/core.js @@ -0,0 +1,121 @@ +var { Core, ALL } = require('../dist/core.umd.js') + +const createModuleMock = () => { + const module = { + started: 0, + stopped: 0, + }; + return Object.assign(module, { + start() { + module.started += 1; + }, + stop() { + module.stopped += 1; + }, + }); +}; + +let core; +describe('Testing Core', function () { + + beforeEach(function () { + core = new Core(); + }); + + afterEach(function () { + }); + + it('should call start of that module', function () { + const module = createModuleMock(); + core.start(module); + + expect(module.started).toBe(1); + }); + + it('should fail if start is undefined', function () { + const module = createModuleMock(); + delete module[`start`]; + + expect(function () { + core.start(module); + }).toThrow(); + }); + + it('should call stop of that module', function () { + const module = createModuleMock(); + const id = core.start(module); + core.stop(id); + + expect(module.stopped).toBe(1); + }); + + it('should not fail if stop is undefined', function () { + const module = createModuleMock(); + delete module[`stop`]; + const id = core.start(module); + core.stop(id); + + expect(module.started).toBe(1); + }); + + it('should use the name as id if it was provided', function () { + const name = `myName`; + const module = createModuleMock(); + const id = core.start(module, { name }); + + expect(id).toBe(name); + }); + + it('should allow multiple instances start', function () { + const module = createModuleMock(); + core.start(module); + core.start(module); + + expect(module.started).toBe(2); + }); + + it('start multiple instances with the same name should throw', function () { + const name = `myName`; + const module = createModuleMock(); + core.start(module, { name }); + + expect(function () { + core.start(module, { name }); + }).toThrow(); + }); + + it('should silently proceed if stop is called on something already stopped', function () { + const module = createModuleMock(); + const id = core.start(module); + core.stop(id); + core.stop(id); + + expect(module.stopped).toBe(1); + }); + + + it('stop should receive as first argument the return of the start', function () { + const x = Symbol() + const module = { + start() { + return x; + }, + stop(arg1) { + expect(arg1).toBe(x); + }, + }; + const id = core.start(module); + core.stop(id); + }); + + it('start should receive an emitter to be able to communcicate with other modules', function () { + const module = { + start(emitter) { + expect(emitter.on).toBeDefined(); + expect(emitter.off).toBeDefined(); + expect(emitter.emit).toBeDefined(); + }, + }; + core.start(module); + }); +}); diff --git a/tests/core/coreSpec.js b/tests/core/coreSpec.js deleted file mode 100644 index d8062b2..0000000 --- a/tests/core/coreSpec.js +++ /dev/null @@ -1,291 +0,0 @@ -var {CoreClass} = require('../../dist/core.umd.js') - -let Core; -describe('Testing Core', function() { - - beforeEach(function () { - Core = new CoreClass(); - }); - - afterEach(function() { - Core.stop(); - }); - - it('Should create a new module', function() { - Core.register('tweet', function() {}); - - expect(Core.modules['tweet']).not.toBeUndefined(); - }); - - it('Should return false and throw a log if the module is already registered', function() { - //spyOn(err); - Core.register('tweet', function() {}); - - expect(Core.register('tweet', function() {})).toBeFalsy(); - //expect(err).toHaveBeenCalled(); - }); - - it('Should start a new module', function() { - Core.register('tweet', function() {}); - Core.start('tweet'); - - expect(Core.moduleInstances.tweet).not.toBeUndefined(); - }); - - it('Should return false and throw a log if the module is already started', function() { - //spyOn(err); - Core.register('tweet', function() {}); - Core.start('tweet'); - - expect(Core.start('tweet')).toBeFalsy(); - //expect(err).toHaveBeenCalled(); - }); - - it('Should stop a new module', function() { - Core.register('tweet', function() {}); - Core.start('tweet'); - Core.stop('tweet'); - - expect(Core.moduleInstances.tweet).toBeUndefined(); - }); - - it('Should return false and throw a log if the module is already stopped', function() { - //spyOn(err); - Core.register('tweet', function() {}); - Core.start('tweet'); - Core.stop('tweet'); - - expect(Core.stop('tweet')).toBeFalsy(); - //expect(err).toHaveBeenCalled(); - }); - - - it('Should start non singleton modules with an alias', function() { - Core.register('user', function () { - return { - "init": function() {} - } - }, true); - Core.start('user', 'Moritz'); - Core.stop('Moritz'); - - expect(Core.stop('Moritz')).toBeFalsy(); - }); - - it('Should not return false and when a factory is already used with different alias', function() { - Core.register('user', function () { - return { - "init": function() { - return true; - } - } - }, true); - Core.start('user', 'Moritz'); - - - expect(Core.start('user', 'Max')).toBeTruthy(); - Core.stop('Moritz'); - Core.stop('Max'); - //expect(err).toHaveBeenCalled(); - }); - - xit('Should start all modules', function() { - Core.register('tweet1', function() {}); - Core.register('tweet2', function() {}); - - Core.startAll(); - - expect(Core.moduleInstances.tweet1).not.toBeNull(); - expect(Core.moduleInstances.tweet2).not.toBeNull(); - }); - - it('Should start all modules using the method start if no parameter is passed', function() { - Core.register('tweet1', function() {}); - Core.register('tweet2', function() {}); - - Core.start(); - - expect(Core.moduleInstances.tweet1).not.toBeNull(); - expect(Core.moduleInstances.tweet2).not.toBeNull(); - }); - - xit('Should stop all modules', function() { - Core.register('tweet1', function() {}); - Core.register('tweet2', function() {}); - - Core.startAll(); - Core.stopAll(); - - expect(Core.moduleInstances.tweet1).toBeUndefined(); - expect(Core.moduleInstances.tweet2).toBeUndefined(); - }); - - it('Should stop all modules using the method stop if no parameter is passed', function() { - Core.register('tweet1', function() {}); - Core.register('tweet2', function() {}); - - Core.start(); - Core.stop(); - - expect(Core.moduleInstances.tweet1).toBeUndefined(); - expect(Core.moduleInstances.tweet2).toBeUndefined(); - }); - - it('Should trigger init when the module is started', function() { - var spying = { - tweet: function() {} - }; - - spyOn(spying, 'tweet'); - Core.register('tweet', function() { - return { - init: function() { - spying.tweet(); - } - }; - }); - - Core.start('tweet'); - - expect(spying.tweet).toHaveBeenCalled(); - }); - - it('Should trigger init from the modules that where started', function() { - var spying = { - tweet1: function() {}, - tweet2: function() {}, - tweet3: function() {} - }; - - spyOn(spying, 'tweet1'); - spyOn(spying, 'tweet2'); - spyOn(spying, 'tweet3'); - - Core.register('tweet1', function() { - return { - init: function() { - spying.tweet1(); - } - }; - }); - - Core.register('tweet2', function() { - return { - init: function() { - spying.tweet2(); - } - }; - }); - - Core.register('tweet3', function() { - return { - init: function() { - spying.tweet3(); - } - }; - }); - - Core.start('tweet1'); - Core.start('tweet3'); - - expect(spying.tweet1).toHaveBeenCalled(); - expect(spying.tweet2).not.toHaveBeenCalled(); - expect(spying.tweet3).toHaveBeenCalled(); - }); - - it('Should trigger destroy when module is stoped', function() { - var spying = { - tweet: function() {} - }; - - spyOn(spying, 'tweet'); - Core.register('tweet', function() { - return { - destroy: function() { - spying.tweet(); - } - }; - }); - - Core.start('tweet'); - Core.stop('tweet'); - - expect(spying.tweet).toHaveBeenCalled(); - }); - - it('Should trigger destroy from the modules that where stopped', function() { - var spying = { - tweet1: function() {}, - tweet2: function() {}, - tweet3: function() {} - }; - - spyOn(spying, 'tweet1'); - spyOn(spying, 'tweet2'); - spyOn(spying, 'tweet3'); - - Core.register('tweet1', function() { - return { - destroy: function() { - spying.tweet1(); - } - }; - }); - - Core.register('tweet2', function() { - return { - destroy: function() { - spying.tweet2(); - } - }; - }); - - Core.register('tweet3', function() { - return { - destroy: function() { - spying.tweet3(); - } - }; - }); - - Core.start('tweet1'); - Core.start('tweet2'); - Core.start('tweet3'); - - Core.stop('tweet1'); - Core.stop('tweet3'); - - expect(spying.tweet1).toHaveBeenCalled(); - expect(spying.tweet2).not.toHaveBeenCalled(); - expect(spying.tweet3).toHaveBeenCalled(); - }); - - describe('Testing return in Start and Stop methods', function() { - it('Should return in the Start method the value returned from the init method inside the module', function() { - Core.register('tweet', function() { - return { - init: function() { - return true; - } - } - }); - - expect(Core.start('tweet')).toBeTruthy(); - - }); - - it('Should return in the Stop method the value returned from the destroy method inside the module', function() { - Core.register('tweet', function() { - return { - destroy: function() { - return true; - } - } - }); - - Core.start('tweet') - expect(Core.stop('tweet')).toBeTruthy(); - - }); - }); -}); diff --git a/tests/jasmine.json b/tests/jasmine.json index a3ec7e7..f3280e6 100644 --- a/tests/jasmine.json +++ b/tests/jasmine.json @@ -1,7 +1,7 @@ { "spec_dir": "tests", "spec_files": [ - "**/*[sS]pec.js" + "**/*.js" ], "helpers": [ diff --git a/tests/sandbox/sandboxSpec.js b/tests/sandbox/sandboxSpec.js deleted file mode 100644 index d686df1..0000000 --- a/tests/sandbox/sandboxSpec.js +++ /dev/null @@ -1,292 +0,0 @@ -var {Core} = require('../../dist/core.umd.js') - -describe('Testing Sandbox', function() { - afterEach(function() { - Core.stop(); - Core.modules = {}; - }); - - it('Should return an instance of sandbox as parameter inside modules', function() { - Core.register('tweet', function(sandbox) { - return { - init: function() { - expect(sandbox).toBeTruthy(); - } - } - }); - - Core.start('tweet'); - }); - - it('Should listen to notification from other modules', function() { - var spying = { - newTweet: function() {} - }; - - spyOn(spying, 'newTweet'); - - Core.register('tweet', function(sandbox) { - return { - init: function() { - sandbox.notify({ - type: 'new-tweet', - data: {} - }); - } - } - }); - - Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen('new-tweet', this.newTweet); - }, - - newTweet: function() { - spying.newTweet(); - } - } - }); - - Core.start('tweet-list'); - Core.start('tweet'); - - expect(spying.newTweet).toHaveBeenCalled(); - }); - - it('Should listen to multiple notifications using array as parameter', function() { - var spying = { - newTweet: function() {} - }; - - spyOn(spying, 'newTweet'); - - Core.register('tweet', function(sandbox) { - return { - init: function() { - sandbox.notify({ - type: 'new-tweet', - data: {} - }); - - sandbox.notify({ - type: 'new-tweet2', - data: {} - }); - } - } - }); - - Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen(['new-tweet', 'new-tweet2'], this.newTweet); - }, - - newTweet: function() { - spying.newTweet(); - } - } - }); - - Core.start('tweet-list'); - Core.start('tweet'); - - expect(spying.newTweet.calls.count()).toEqual(2); - }); - - it('Should notify other modules from a specific notification', function() { - var spying = { - newTweet: function() {}, - newTweet2: function() {} - }; - - spyOn(spying, 'newTweet'); - spyOn(spying, 'newTweet2'); - - Core.register('tweet', function(sandbox) { - return { - init: function() { - sandbox.notify({ - type: 'new-tweet', - data: {} - }); - } - } - }); - - Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen('new-tweet', this.newTweet); - sandbox.listen('new-tweet2', this.newTweet2); - }, - - newTweet: function() { - spying.newTweet(); - }, - - newTweet2: function() { - spying.newTweet2(); - } - } - }); - - Core.start('tweet-list'); - Core.start('tweet'); - - expect(spying.newTweet).toHaveBeenCalled(); - expect(spying.newTweet2).not.toHaveBeenCalled(); - }); - - it('Should pass data through notifications', function() { - Core.register('tweet', function(sandbox) { - return { - init: function() { - sandbox.notify({ - type: 'new-tweet', - data: { - a: true, - b: false - } - }); - } - } - }); - - Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen('new-tweet', this.newTweet); - }, - - newTweet: function(data) { - expect(data.a).toBeTruthy(); - expect(data.b).toBeFalsy(); - } - } - }); - - Core.start('tweet-list'); - Core.start('tweet'); - }); - - it('Should not overwrite an existing listening notification', function() { - var spying = { - toBeCalled: function() {}, - notToBeCalled: function() {} - }; - - spyOn(spying, 'toBeCalled'); - spyOn(spying, 'notToBeCalled'); - - Core.register('tweet', function(sandbox) { - return { - init: function() { - sandbox.notify({ - type: 'new-tweet', - data: {} - }); - } - } - }); - - Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen('new-tweet', this.toBeCalled); - sandbox.listen('new-tweet', this.notToBeCalled); - }, - - toBeCalled: function() { - spying.toBeCalled(); - }, - - notToBeCalled: function() { - spying.notToBeCalled(); - } - } - }); - - Core.start('tweet-list'); - Core.start('tweet'); - - expect(spying.toBeCalled).toHaveBeenCalled(); - expect(spying.notToBeCalled).not.toHaveBeenCalled(); - }); - - it('Should overwrite an existing notification if force parameter is passed', function() { - var spying = { - toBeCalled: function() {}, - toOverwrite: function() {} - }; - - spyOn(spying, 'toBeCalled'); - spyOn(spying, 'toOverwrite'); - - Core.register('tweet', function(sandbox) { - return { - init: function() { - sandbox.notify({ - type: 'new-tweet', - data: {} - }); - } - } - }); - - Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen('new-tweet', this.toBeCalled); - sandbox.listen('new-tweet', this.toOverwrite, this, true); - }, - - toBeCalled: function() { - spying.toBeCalled(); - }, - - toOverwrite: function() { - spying.toOverwrite(); - } - } - }); - - Core.start('tweet-list'); - Core.start('tweet'); - - expect(spying.toBeCalled).not.toHaveBeenCalled(); - expect(spying.toOverwrite).toHaveBeenCalled(); - }); - - it('Should be able to choose the "this" value inside an notification', function() { - var thisReference = {}; - - Core.register('tweet', function(sandbox) { - return { - init: function() { - sandbox.notify({ - type: 'new-tweet', - data: {} - }); - } - } - }); - - Core.register('tweet-list', function(sandbox) { - return { - init: function() { - sandbox.listen('new-tweet', this.newTweet, thisReference); - }, - - newTweet: function() { - expect(this).toBe(thisReference); - } - } - }); - - Core.start('tweet-list'); - Core.start('tweet'); - }); - -}); diff --git a/tools/rollup.config.js b/tools/rollup.config.js index 43a1fbf..691d648 100644 --- a/tools/rollup.config.js +++ b/tools/rollup.config.js @@ -1,82 +1,82 @@ // rollup.config.js import _package from "../package.json"; -const {version, name, license} = _package; +const { version, name, license } = _package; const GLOBAL_NAME = `Core`; const banner = `/* ${name} v${version} ${new Date().toJSON()} licensed ${license} */`; - const commonOutputOptions = { - // core output options - name : GLOBAL_NAME, - // globals: [], +const commonOutputOptions = { + // core output options + name: GLOBAL_NAME, + // globals: [], - // advanced output options - // paths: {}, - banner, - // footer: ``, - // intro: ``, - // outro: ``, - // sourcemap, - // sourcemapFile, - interop: false, - extend: false, + // advanced output options + // paths: {}, + banner, + // footer: ``, + // intro: ``, + // outro: ``, + // sourcemap, + // sourcemapFile, + interop: false, + extend: false, - // danger zone - // exports, - // indent, - strict: true, - // freeze, - namespaceToStringTag: false + // danger zone + // exports, + // indent, + strict: true, + // freeze, + namespaceToStringTag: false - // experimental - // entryFileNames, - // chunkFileNames, - // assetFileNames + // experimental + // entryFileNames, + // chunkFileNames, + // assetFileNames }; export default { // can be an array (for multiple inputs) - // core input options - input: `src/core/core.js`, // required - // external: [], - //plugins: [], + // core input options + input: `src/core.js`, // required + // external: [], + //plugins: [], - // advanced input options - // onwarn, - // perf, + // advanced input options + // onwarn, + // perf, - // danger zone - // acorn, - // acornInjectPlugins, - treeshake: { - pureExternalModules: false, - propertyReadSideEffects: false // assume reading properties has no side effect - }, - // context, - // moduleContext, + // danger zone + // acorn, + // acornInjectPlugins, + treeshake: { + pureExternalModules: false, + propertyReadSideEffects: false // assume reading properties has no side effect + }, + // context, + // moduleContext, - output: [ // required (can be an array, for multiple outputs), - Object.assign({ - format: `es`, - file : `dist/core.js`, - }, commonOutputOptions), - Object.assign({ - format: `iife`, - file : `dist/core.iife.js`, - }, commonOutputOptions), - Object.assign({ - format: `umd`, - file : `dist/core.umd.js`, - amd: { - id: GLOBAL_NAME - } - }, commonOutputOptions) - ], + output: [ // required (can be an array, for multiple outputs), + Object.assign({ + format: `es`, + file: `dist/core.es.js`, + }, commonOutputOptions), + Object.assign({ + format: `iife`, + file: `dist/core.iife.js`, + }, commonOutputOptions), + Object.assign({ + format: `umd`, + file: `dist/core.umd.js`, + amd: { + id: GLOBAL_NAME + } + }, commonOutputOptions) + ], - watch: { - // chokidar, - // include, - // exclude, - clearScreen: true - } + watch: { + // chokidar, + // include, + // exclude, + clearScreen: true + } };