Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/bassdman/modulerizr

Adds modularisation to legacy projects in a very simple way.
https://github.com/bassdman/modulerizr

Last synced: 13 days ago
JSON representation

Adds modularisation to legacy projects in a very simple way.

Awesome Lists containing this project

README

        

# Modulerizr

> Integrate a powerful, component based architecture to your legacy project in just a few steps

[![npm version](https://img.shields.io/npm/v/modulerizr.svg)](https://www.npmjs.com/package/modulerizr)
[![npm downloads](https://img.shields.io/npm/dt/modulerizr.svg)](https://www.npmjs.com/package/modulerizr)
[![npm downloads](https://img.shields.io/github/license/mashape/apistatus.svg)](https://www.npmjs.com/package/modulerizr)

Quick Links
- [Configuration](#modulerizrconfig)
- [Features](#features)
- [Plugins](#modulerizr-plugins)
- [ModulerizrWebpackPlugin](https://www.npmjs.com/package/modulerizr-webpack-plugin)

## Install

``` shell
npm install modulerizr --save-dev
```

## The Problem
When designing larger websites you will end up in many problems you have to challenge, like
- very large files imports
- overwriting css-rules,
- overwriting/global scoped javascript-variables,
- serverside syntax like php,jsp,... mixed with html-content
- ...
Let's consider the html below:
``` html


This could be a legacy page in your project



.aPseudoLocalClass{
color: green
}




{+include-head}


My color is pink, not green :D


...
//oh, there was a global scoped variable. Now I have overwritten it an a onclick-script does does something wrong... *angry-smiley*
{!testmode}
var color = '{{serversideColor}}';
{/!testmode}
{testmode}
var color = '{{anyOtherColor}}';
{/testmode}
...

{+include-footer}

```

Many solutions exist to reduce these problems in web projects, one of the most important ones is __modularisation__.
There are many good frameworks that use this pattern and do a veeeery, very good job with it
- Angular
- Vue
- React
- ... many other ones

BUT: These Frameworks DON'T WORK GOOD WITHIN LEGACYPROJECTS. Have you ever seen Serverside-Syntax in Vue or Angular-Templates. No. If you have a legacy project and want to change it to a modular system, you will have big problems. That means you can not easily switch to modularisation when all your architecture is currently designed with php.

Here's is a solutionen - where you can have serverside syntax AND modularisation without big effort.

## The solution
__Modulerizr__.

While angular, react,... give you many great features from the scratch without having to think about it, Modulerizr does it vice versa. It gives you a simple infrastructure for modularisation and you can add the features you want.

Because of this, you can append it to almost every legacy project you can imagine.

## Usage

Imagine the following html-page "startpage.hml":

``` html


Startpage
...


{+include-head}

Some Text from component1
{{serversideText}}
{+include-footer}

```
>The Brackets { } show serverside syntax. It could also be php-syntax,...

The component "custom-component-1.component.hml":
``` html


.color{color:green}


var x = "a scoped variable";

```

And the component "custom-component-2.component.hml":
``` html


.color{color:red}


var x = "another scoped variable; was not defined before";


{{Some serverside Syntax here}}

```

Then run modulerizr:
``` javascript
const { modulerizr } = require('modulerizr');

modulerizr.run({
"src": ["startpage.html"],
"components": ["*.component.html"],
"dest": "./dest/",
});
```
> More ways to run modulerizr you see in the next section ["How to run modulerizr"](#how-to-run-modulerizr)

Voilà, your're done. This will be rendered to:
``` html


Startpage
...


{+include-head}



.color[data-v-1e34329b]{color:red}


(function(){
var x = "a scoped variable";
})();

Some Text from component1




.color[data-v-93d13c56]{color:red}


(function(){
var x = "another scoped variable; was not defined before";
})();

{{serversideText}}

{{Some serverside Syntax here}}

{+include-footer}

```

If you need some more specials features, just [add a plugin](#plugins) that does what you need.

## How to run modulerizr
There are multiple ways to run it. With Terminal or with node.

### Run in Terminal
Before running it, add a modulerizr.config.js in the root-folder.
``` javascript
module.exports = {
"src": ["**/*.src.html"],
"components": ["*.component.html"],
"dest": "./dest/",
}
```

##### Variant 1 - globally installed:
Install the package
``` shell
npm install modulerizr --global
```
And run it
``` shell
modulerizr run
```
##### Variant 2 - locally installed:
Install the package
``` shell
npm install modulerizr --save-dev
```

Add a script to the package.json
``` javascript
{
...
"scripts": {
"modulerizr": "modulerizr run"
}
...
}
```
and run it
``` shell
npm run modulerizr
```

##### Commandline-Parameters
``` shell
#both overwrites debug-Attribute in config

#debug mode: shows logs;
modulerizr --debug

#production mode: hides logs; default;
modulerizr --production
```
#### Node
Here's how
``` javascript
const { modulerizr } = require('modulerizr');
const config = {...} // some config
modulerizr.run(config)
```

### Modulerizr.config
You can run one config or an array of configs
``` javascript
const { modulerizr } = require('modulerizr');
const config1 = {...} // some config
const config2 = {...} // some other config

//executes one config
modulerizr.run(config1);

//executes multiple configs
modulerizr.run([config1,config2]);
```
#### Config attributes
##### src
All src-files that will be prerendered. They will be copied into the destination-folder. [Glob-Syntax](https://www.npmjs.com/package/glob).
Type String or Array. Required.
``` javascript
{
...
// it can be a string
"src": "**/*.allsrcfiles.html",

//or an array of strings
"src": ["srcfile1.html","srcfile2.html","srcfile3.html"]
...
}
```

##### components
All component-files. [Glob-Syntax](https://www.npmjs.com/package/glob).
Type: String or Array. Optional. (But there will be a warning if you don't define it)
``` javascript
{
...
// it can be a string
"components": "**/*.component.html",

//or an array of strings
"components": ["comp1.component.html","comp2.component.html","comp3.component.html"]
...
}
```

##### dest
The folder where the files will be rendered to. MUST BE AN ABSOLUTE PATH.
Type: String. Optional. Defaults to "dest"
``` javascript
{
...
"dest": "./dest",
...
}
```

##### debug
Debugmode. Shows logs if debug == true.
Will be overwritten by the [--debug](#commandline-parameters) or [--production]((#commandline-parameters)) Parameter in command line.
Type: Boolean. Default: false.
``` javascript
{
...
"debug": true,
...
}
```

##### plugins
If you need a custom feature, you can add it via plugin.
Type: Array.
``` javascript
const { modulerizr } = require("modulerizr");

modulerizr.run({
...
//Add your plugins here
"plugins": [DebugPlugin()],
...
})
```

##### defaultComponentWrapper
By Default, components are wrapped by a div-tag. To change this, a component needs a "wrapper"-attribute or you can you can use a default-wrapper-tag for each component.
This will be overwritten by the tag assigned in the component.

``` javascript
const { modulerizr } = require("modulerizr");

modulerizr.run({
...
//Now all you components will be wrapped by a span
"defaultComponentWrapper": "span",
...
})
```

##### maxRecursionLevel
What happens if you add component X in component X and the content does not change? We have an infinte-loop.

```html






...




```

We assume this is not expected - that's why there is a maximum recursion level. This example above has a recurison level of 6 (until the three dots "...") because there are 6 levels of components.
By default ther is a maximumRecursionLevel of 100 - if you have more, there will be an error because we expect, that there is sth wrong.

Maybe there is a usecase where you need more levels. You can increase this level in the config with the maxRecursionLevel-attribute.

``` javascript
const { modulerizr } = require("modulerizr");

modulerizr.run({
...
//Now you can have 500 component-levels. Yippeeee
"maxRecursionLevel": "500",
...
})
```

## Features
To understand the the next features, it is good to know the differences between components and src-files:
- Src-Files:
- Any html how you already use it
- They are the Root-Files that will be prerendered.
- The transpilation of these files will be added in the dest-folder
- Many features like scoped variables,... don't work in src-files (if you don't change this via config)

- Components
- It is wrapped by a template-tag with some attributes
- Currently each component must have its own file - this will be changed in future
- A component can include other components
- All features like scoping,... work in components
- A component by itself won't be rendered - it (or a parent component) must be included into a src-file

### Basics

Without one of the next features, a component is just outsourced html from the original file - like a php-include. So we make sure, that the rendering process does not affect legacy files.

Example:
src-file.html
``` html

...

some text

some text

```
component1.component.html
```html

This is component1

```
dest-file:
``` html

...

some text
This is component1
some text

```

To be honest: This feature by itself is not better then a php-include, it wouldn't make sense writing this package to add a feature that can be added without effort.

Let's add some more "magic":

### Components
Sorry, before adding "magic", we need the basics of components.
A component is always wrapped by a template-tag and has a uniqe name.
``` html

here comes the content

```
If the name is missing or a component with this name already exists, there will be an error.
> Until now just one component per file is possible. Also inline components in a src-file are not possible. This will change in future.

#### Slots
The first "magic", well known from vue, web-components,...
Sometimes you want to do sth with the innerHTML in the component declaration.

##### Default Slot:
Anywhere in the src-file:
```html
...


This content will be bold, even though no class or style can be seen in the src-file;

...
```
In the component file:
```html





```
Will be rendered to:
```html
...


This content will be bold, even though no class or style can be seen in the src-file;


...
```

##### Named slots
Sometimes you need more slots per component. In this case, you can add named slots.
Anywhere in the src-file:
```html
...


This text is written before a static text.


This text is written after a static text.

...
```
In the component file:
```html


This Text is in the middle



```
Will be rendered to:
```html
...
This text is written before a static text.

This Text is in the middle

This text is written after a static text.

...
```

#### Wrapper
Right now the default wrapper-element is a div. But for some components you may want another tag then div.
Add the "wrapper"-attribute to a component assignment to change the wrapper attribute.
```html
...
This is a bold header
...
```
will be rendered to
```html
...



This is a bold header

...
```
If you want to change the wrapper-element for all elements, [set the "defaultComponentWrapper"-Attribute](#defaultComponentWrapper) in the plugin-configuration.
If both the config attribute ("defaultComponentWrapper") is set and the component attribute ("m-wrapper"), the component attribute will be used.

#### Scoped Styles
What happens if you have 2 components with the same style declaration, but different value? The style will be overwritten. :(

##### Problem
Component A
```html


.textColor{color: red;}

This Text is red.

```
Component B
```html


.textColor{color: green;}

This Text is green.

```
Src-file:
```html
...


...
```
This will be rendered to
```html
...

.textColor{color: red;}

This Text is red.

.textColor{color: green;}

This Text is green.

...
```

##### Solution
If you want scoped styles, just add a "scoped" attribute to the "style"-tag.
Component A
```html


.textColor{color: red;}

This Text is red.

```
Component B
```html


.textColor{color: green;}

This Text is green.

```

This will be rendered to
```html
...

.textColor [data-v-12345]{color: red;}

This Text is red.

.textColor [data-v-67890]{color: green;}

This Text is green.

...
```

##### Efficency
What happens if you add the same component multiple times?
```html
..

...
```
Will the same styles exist multiple times?
```html
...

.textColor [data-v-67890]{color: green;}

This Text is green.

.textColor [data-v-67890]{color: green;}

This Text is green.

.textColor [data-v-67890]{color: green;}

This Text is green.

...
```
No. Same styleblocks with attribute "scoped" will just exist once. the example above would look like this:
```html
...

.textColor [data-v-67890]{color: green;}

This Text is green.

This Text is green.

This Text is green.

...
```

#### Scoped Scripts

If you add a raw script-tag in a component, it can have side effects to other components. Variables are global scoped and so they can overwrite other variables.

##### Example
Parent-Component
```html


var text = "Hello Parent";



console.log(text);

```

Child-Component
```html


var text = "Hello child";
console.log(text);

```

This would be rendered like this:
```html

// Declaration in the parent component
var text = "Hello Parent";


// Declaration in the child component
var text = "Hello child";
console.log(text);

// Oh no, the text in the parent component has been overwritten. That's not expected;
console.log(text);

```
There are two Problems:
- the global Scope is polluted
- variables can be overwritten what is not expected

##### Solution
Scoped Scripts: Just add a "scoped"-Attribute to the script and this can not happen anymore.

I this case, the parent component stays the same. In the child component we add a "scoped"-Attriubte

Child-Component
```html


var text = "Hello child";

```

Would be rendered to
```html

// Declaration in the parent component
var text = "Hello Parent";



(function(window){
// This is added when you use a "scoped"-Attribute.
//It gives you important component information in javascript.
var _m = {
id: '12345',
name: 'child-component',
$el: document.getElementById('12345'),
slots: {
_default: "<script m-scoped>var text = "Hello child";"
},
attributes: {
"attributeKey1": "attributeValue1"
...
}
};

var text = "Hello child";
console.log(text);
})(window);

// As expected, "Hello parent" will be logged here
console.log(text);

```

#### One Time Rendering
We work on a legacyproject and the codbease has no / not many components - and many scripts are assigned via script-tag.

Some scripts are only necessary for a specific features - for example jquery UI for a slider.

We can use modulerizr to only add external scripts into the src-files, when they're used in a component.
Let's imagine a slider component that needs the external jquery-ui-lib.
```html




Here we add the component elements.
...

```
Adding external scripts and styles in templates work as expected - but you have to take care of one scenario:
Using a component multiple times per page.
Imagine this:
```html

...

```
This would be rendered to
```html


Here we add the component elements.
...

...


Here we add the component elements.
...

```
By default external stylesheets are just loaded once per src-file (when assigend in a component). In case of scripts you have to assign them with a "once"-Attribute to assure that they are just loaded once.

```html


Here we add the component elements.
...

```

Declared multiple times in a src-file it would be rendered like this:
```html


Here we add the component elements.
...

...

Here we add the component elements.
...

```

#### Component Config
Your Component may need some specific configuration. You can add this in your component by the attribute "m-componentconfig".
```html


/*Add your component configuration here*/

```

This is useful if you for example want to preload data and use it with the modulerizr-jsrender-plugin. This script must be a node script and export an object.

```html


//This data could also be fetched from anywhere else asynchronous
const data = {
name: "dieter",
kids: [{name:'heinz'},{name:'gustav'}]
};

module.exports = {
data(){
return data;
}
}

```

This data will also be rendered in the scoped scripts:
```html

(function(window){
var _component = {
id: "c7350017",
name: "any-component",
$el: document.getElementById("c7350017"),
data: {"name":"dieter","kids":[{"name":"heinz"},{"name":"gustav","def":"ghi"}]},
attributes: {},
slots: {"_default":""},
};
})(window);

```
### Modulerizr-Plugins
You can use any other webpack-plugin to add more features you want, for example for bundling,...
But there are some specific modulerizr-plugins that exist.
#### Modulerizr-jsrender-plugin
The [modulerizr-jsrender-plugin](https://www.npmjs.com/package/modulerizr-jsrender-plugin) gives you the chance to add template-syntax in your templates. It is based on [JS-Render](https://www.npmjs.com/package/jsrender).
```html
Component:

Hello {{:name}}.
{{if age> 30}}
You are reeeeally old.
{{/if}}
{{if age <= 30}}
In Germany they would call you "Jungspund".
{{/if}}

Src:



Will be rendered to:



Hello Peter.
In Germany the would call you "Jungspund".


```
See more information about this plugin [here](https://www.npmjs.com/package/modulerizr-jsrender-plugin)

#### Use Webpack Plugins
A Modulerizr-Plugin is just a webpack-plugin. That means, you can add any webpack plugin to customize your files. [Here's a list of webpack-plugins](https://github.com/jantimon/html-webpack-plugin#plugins), that have nothing to do with modulerizr but can also be added in the plugins.
```javascript
// This works:
const { modulerizr } = require('modulerizr');
const {StyleExtHtmlWebpackPlugin } = require('style-ext-html-webpack-plugin');
modulerizr.run({
//...some other config...
plugins: [new StyleExtHtmlWebpackPlugin()]
});

```

#### Write your own Plugin
[Read here how to write your own Plugins](https://github.com/bassdman/modulerizr/blob/master/README.ownplugin.md).

### Features in future
- inline-templates in src-files, marked with a "inline-template"-Attribute.
- multiple components per file
- support attribute component declarations
- scoped link tags

## Contribution
- You have some ideas how to make modulerizr more simple?
- You have ideas for a new feature / new modulerizr-plugin?
- You found a bug?
- Or you have some general demands/questions?

Then [create an issue](https://github.com/bassdman/modulerizr/issues) or contact me.
Or if you have time and a good idea, then you can of course create a new plugin. This would be awesome :D

## Last but not least
Happy Coding. Let's get rid of your legacy code in your Projects :D