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:
- Doop tries to have the lowest understanding cost possible
- Doop units are designed to be as generic and transferable between projects as possible
Terminology:
- The Doop documentation endeavour to use requirement levels that abide by the definitions specified by RFC 2119 (Requirement Levels)
Major releases:
- V3 - Cubert - Current stable release (Vue@3 compatible frontend)
- V2 - Bender - Previous stable release (Vue@2 frontend)
- V1 - Amy - Initial Doop implementation
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.
MFDC relies on various common NPM modules across all its projects.
You can read more about how MFDC structures its project in the Momsronomicon.
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 |
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 |
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.
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:
- Names of exported items (such as components, filters, services and templates) are all automatically derive from the filename and transformed via CamelCase. The name can be overridden by specifying the name attribute e.g.
<service name="myService"/>
to export asvm.$myService
. - Services are accessible via either
vm.$SERVICE
orapp.service.$SERVICE
- All registered filters are likewise available via
vm.$filters.FILTER
orapp.filter.FILTER
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);
Doop is exposed as a global level app
object which contains the following functions:
The App object is a global, available everywhere in the project. It is a mutated version of the ExpressJS App object and eventer classes.
Globally available instance of Generic-Cache
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
.
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.
Emit an event using eventer.
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.
Module name prefixed version of app.log()
.
Globally available version of Chalk.
Globally available version of dumper.js
Globally available version of debug
Globally available version of Crash.
Use app.log.errorCrash(Error)
to perform a fatal error.
Same as app.log()
but with a "[WARNING]" prefix.
Prefixed version of app.log.warn()
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.
Register an event listener using eventer.
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() { ... })
.
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.