MomsFriendlyDevCo / Doop

The MEVN micro-framework used by MomsFriendlyDevCo
4 stars 5 forks source link



Doop@3!

The MEVN micro-framework used by MomsFriendlyDevCo.

If we hit that bullseye, the rest of the dominoes will fall like a house of cards. Checkmate - Zapp Brannigan

Basic principles:

Terminology:

Major releases:

General concept

Doop is made up of two types of files. Files ending in .vue are frontend Vue framework(esk) and .doop are backend Express framework(esk).

Within these two file types there can be blocks of code which expose things like controllers, schemas, models, CSS, HTML templates and so on.

Generally all areas of an application are exposed at the root level (e.g. widgets/) with shared components living inside directories such as filters, widgets and so on.

NPM modules

MFDC relies on various common NPM modules across all its projects.

You can read more about how MFDC structures its project in the Momsronomicon.

Project directories and files

The project tree breakdown is listed below. For each path an 'edit chance' is given (i.e. the possibility of the developer needing to change the contents of that directory) and a brief description.

Path or Glob Description
/ The project root
/app/ Core initialization files
/assets/ Generic static assets such as logo images and favicons
/build/ Build scripts
/cache/ Generic cache handling
/config/index.js Base config file showing default variables subsequently overridden by each NODE_ENV config file
/config/*.js Other config files, loaded selectively based on the NODE_ENV environment variable
/config/private.js Private config details. This file is listed in .gitignore and should never be checked in to GitHub as it can contain private details - e.g. database connection passwords or API keys
/config/production.conf.js Production server config details. This should include settings to enable all minimizers and other optimizations for production-ready code
/config/ Storage for all config scripts read at startup, see config/index.js for default config setup. Other files are read depending on the NODE_ENV setting
/dashboard/ Front end application entry point
/data/ Generic data for the project - could contain resource files used during automated builds
/db/ The main database driver and schema loader
/debug/ Various debugging utilities
/directives/ Shared frontend Vue directives
/dist/ Generated files directory. Do not edit the contents of this directory as it is auto-generated by Gulp
/docs/ Any miscellaneous files not relevant to the operation of the project but which need to be retained e.g. scope documents, ERD diagrams
/errors/ Error handling and reporting
/filters/ Shared frontend Vue filters
/fonts.fa5/ Font-Awesome 5
/fonts/ Other font files
/.git/ Git storage and meta information
/gulpfile.js Main Gulp build-system config file
/layouts/ Global page layout templates
/layouts/main.html Default page layout template
/middleware/db.*.doop Database middleware
/middleware/ Express and Database middleware
/middleware/express.*.doop Express middleware
/node_modules/ Install directory for all NPM controlled packages
/scenario/ Database scenario templates
/server/ Server bootstrapping scripts
/services/ Shared frontend Vue services (usually available as vm.$SERVICE)
/tests/ Optional testing files
/theme/bootstrap-extensions/ Various CSS files which extend Bootstrap@4
/theme/ Site theme
/theme/theme-overrides/ Various CSS files which fix issues with the main theme
/users User schemas and management
/vendors Externally supported vendor scripts
/widgets Shared frontend Vue services
/**/*.mjs Isomorphic JS files available to frontend/backend as required via import / require respectively
/**/*.doop Backend Doop modules
/**/*.gulp.js Global Gulp build-system task files
/**/*.vue Frontend Vue modules

Modules

The following modules are provided separately and can be optionally included within a project.

Module Default Description
@doop/service-components Yes $components service allowing cross talk and enumeration of components
@doop/service-data Yes Various data manipulation service components
@doop/service-toast Yes $toast service to display simple UI messages
@doop/service-data Yes Various data I/O services - $assign, ${has,get,set}Path, $push
@doop/service-morph Yes Animation library to transform DOM components with animations
@doop/directive-jump Yes v-jump directive to easily scroll around pages using <a/> style anchors

Content Blocks

Backend .doop files

Doop backend files are made of <script/> blocks which execute when a named event is emitted (via app.emit(event)).

For example simple ReST endpoints endpoint are registered like so:

<script lang="js" backend endpoint>
/**
* This function does something
*/
app.get('/api/foobar', (req, res) => {
    // Do something //
});
</script>

Database schemas are registered similarly:

<script lang="js" backend schema>
app.middleware.db.schema('widgets', {
    title: String,
});
</script>

More advanced functions can be registered in a specific place within the load order (see the server file for the exact load order)

<script lang="js" backend on="postServer">
// Server has now loaded
</script>

Some <script/> attributes are provided which makes typing a little less tedious:

Block Alias of Definition
endpoint <script on="endpoints"> An API endpoint
middleware <script on="middleware"> Database / server middleware
schema <script on="schemas"> A database schema

For the server block the on event must be specified and can contain a CSV of multiple events.

For a reference of which events fire and in what order see the server and gulpfile.

Frontend .vue files

Vue files follow the regular Vue Single-Page-Component format with the following block types:

Block Contents Definition
script Frontend javascript A Vue frontend definition (limit of one block per file)
style CSS Global level CSS
template HTML Template file (the file name is used to match it against the controller)

For example the following file registers a Vue component with the name widget:

<script lang="js" frontend>
app.component('widget', {
    // Vue component definition
})
</script>

.vue files can also contain an optional properties which gets specical treatment during the component load phase:

<script lang="js" frontend>
app.component('ordersEdit', {
    route: '/orders/:id',                 // Set up a route for this component automatically
    routeRequiresAuth: true,              // Require a session to access this component (the default), otherwise let guest users view
    // ... Vue component definition ... //
})
</script>

Special component properties supported by Doop:

Property Type Default Description
route String Auto-installed route to access the component
routeRequiresAuth Boolean true Whether the route should redirect to the login page if no session is present

Standard frontend component registration types are:

Type Syntax example Description
Vue Component app.component('name', { ... Vue Component dedinition ... }) Register a Vue frontend component (route key optional)
Doop / Vue filter app.filter('name', (value, options) => {} Register a filter function
Doop / Vue service app.service('$name', { ... definition ... }) Register a service

Notes:

Syntax

All .vue files are compiled via Babel and executed with the following presets:

In essence this is really just an in-built version of the lodash.get function wrapped in regular JavaScript syntax.

// Assuming:
var obj = {
    foo: {
        bar: {
            baz: 42,
        },
    },
};

var baz = obj?.foo?.bar?.baz; //= 42
var nonExistant = obj?.qux?.baz; //= undefined

Chain multiple functions in a pipeline using something like Promise / arrow function syntax:

var result = 'hello'
    |> doubleSay
    |> capitalize
    |> x => exclaim('Howdy or', x);

API

Backend

Doop is exposed as a global level app object which contains the following functions:

app

The App object is a global, available everywhere in the project. It is a mutated version of the ExpressJS App object and eventer classes.

app.cache

Globally available instance of Generic-Cache

app.config

An object used to hold the calculated contents of the /config files. This is typically a combination of the main index.js file, any NODE_ENV specific files and the private.js file. To see the config of the profile you are currently using run gulp app.config.

app.db OR db

In a typical Doop deployment this object represents an object of all loaded models. This may not be the case in all projects, depending on the database driver used.

db is also registered as a global for convenience.

app.emit(event, args...)

Emit an event using eventer.

app.log(...msg)

Various logging functionality. Try to use app.log() instead of console.log() as text output via it is properly prefixed to the running file name.

app.log.as(name, ...msg)

Module name prefixed version of app.log().

app.log.colors

Globally available version of Chalk.

app.log.dump(...msg)

Globally available version of dumper.js

app.log.debug(...msg)

Globally available version of debug

app.log.error(Error)

Globally available version of Crash. Use app.log.errorCrash(Error) to perform a fatal error.

app.log.warn(...msg)

Same as app.log() but with a "[WARNING]" prefix.

app.log.warn.as(module, ...msg)

Prefixed version of app.log.warn()

app.middleware

In a typical Doop deployment this object represents an object of available middleware. Use app.middleware.express or app.middleware.db for the Express and database middlewares respectively.

app.on(event, handler)

Register an event listener using eventer.

Testing + Debugging

Unit tests

Larger projects can populate the /tests/ directory with Mocha + Chai test cases. Tests can also be specified per-unit by adding any file matching the glob *.test.js within their unit directory.

To run a Unit test you will need to have Mocha installed (sudo npm i -g mocha). Either run mocha with no specified files to process all test files or specify the specific test file(s) you wish Mocha to examine as command line arguments.

Run the all the tests with mocha in the project root directory with mocha.

TIP: Unit tests can be skipped temporarily by either adding an 'x' before describe / it or adding .skip as a suffix. e.g. To temporarily skip a test rename the function describe('something something') to xdescribe('something something') or describe.skip('something something'). TIP:: As with the above you can also force Mocha to only run one test - this is useful if your test is deeply nested within an existing file and you don't want to add skip to each other case. To use this add .only as a suffix for the describe / it declaration. e.g. it('should do this', function() { ... }) becomes it.only('should do this', function() { ... }).

Command line debugging

Doop, like most modules, responds to the DEBUG flag used by the Debug module. This turns on verbose logging when running within a console. The DEBUG variable can be set to the specific module you would like to log, a number of them via comma-separated-values or a glob expression. For example DEBUG=doop gulp runs Gulp and tells Doop to run in debug mode.