# 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
```

<!-- - [Quicklink to the API](#api-description) -->

## 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
<html>
    <head>
        <title>This could be a legacy page in your project</title>
        <!-- 
            This Stylesheet is 100kb large because it includes all the styles of your project.
            And you just need ****** 5% oft thes styles on this page 
        -->
        <link rel="stylesheet" type="text/css" href="path/to/your/css/allStyles.css">

        <!-- 
            This script all libraries you need in your project - again 95% that are not used.
        -->
        <script src="path/to/global-scripts.js"></script>

        <style>
            .aPseudoLocalClass{
                color: green
            }
        </style>
    </head>
    <body>
        <!-- 
            Many legacy projects have serverside syntax in the templates PHP, jsp,... most of the time.
            Here shown with the brackets.
        -->
        {+include-head}
            <!-- **** There is a selector with !important to class aPseudoLocalClass in the globalStyles. Now it's pink... -->
            <div class="aPseudoLocalClass">
                My color is pink, not green :D
            </div>
            <script>
                ...
                //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}
                ...
            </script>
        {+include-footer}
    </body>
</html>
```

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
<html>
    <head>
        <title>Startpage</title>
        ...
    </head>
    <body>
        {+include-head}
            <!-- **** There is a selector with !important to class aPseudoLocalClass in the globalStyles. Now it's pink... -->
            <custom-component-1>Some Text from component1</custom-component1>
            <custom-component-2>{{serversideText}}</custom-component2>
        {+include-footer}
    </body>
</html>
```
>The Brackets { } show serverside syntax. It could also be php-syntax,...

The component "custom-component-1.component.hml":
``` html
<template name="custom-component1">
    <style m-scoped>
        .color{color:green}
    </style>
    <script m-scoped>
        var x = "a scoped variable";
    </script>
    <div class="color"><slot></slot></div>
</template>
```

And the component "custom-component-2.component.hml":
``` html
<template name="custom-component2">
    <style m-scoped>
        .color{color:red}
    </style>
    <script m-scoped>
        var x = "another scoped variable; was not defined before";
    </script>
    <div class="color"><slot></slot></div>
    {{Some serverside Syntax here}}
</template>
```

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
<html>
    <head>
        <title>Startpage</title>
        ...
    </head>
    <body>
        {+include-head}
            <div id="1e34329b" data-v-1e34329b data-component="custom-component1">
                <style>
                    .color[data-v-1e34329b]{color:red}
                </style>
                <script>
                    (function(){
                        var x = "a scoped variable";
                    })();   
                </script>
                <div class="color" data-v-1e34329b>Some Text from component1</div>
            </div>
            <div id="93d13c56" data-v-93d13c56  data-component="custom-component2">
                <style>
                    .color[data-v-93d13c56]{color:red}
                </style>
                <script>
                    (function(){
                        var x = "another scoped variable; was not defined before";
                    })();   
                </script>
                <div class="color" data-v-93d13c56>{{serversideText}}</div>
                {{Some serverside Syntax here}}
            </div>
        {+include-footer}
    </body>
</html>
```

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
<component-a>
    <component-a>
        <component-a>
            <component-a>
                <component-a>
                    <component-a>
                        ...
                    </component-a>
                </component-a>
            </component-a>
        </component-a>
    </component-a>
</component-a>
```

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
<html>
    <head>...</head>
    <body>
        some text
        <component-1></component-1>
        some text
    </body>
</html>
```
component1.component.html
```html
<template name="component-1">
    This is component1
</template>
```
dest-file:
``` html
<html>
    <head>...</head>
    <body>
        some text
        This is component1
        some text
    </body>
</html>
```

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
    <template name="xyz">
        here comes the content
    </template>
```
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
...
<make-bold>
    <div> 
        This content will be bold, even though no class or style can be seen in the src-file;
    </div>
</make-bold>
...
```
In the component file:
```html
    <template name="make-bold">
        <div style="font-weight:bold;">
            <slot></slot>
        </div>
    </template>
```
Will be rendered to:
```html
...
<div style="font-weight:bold;">
    <div> 
        This content will be bold, even though no class or style can be seen in the src-file;
    </div>
</div>
...
```

##### Named slots
Sometimes you need more slots per component. In this case, you can add named slots.
Anywhere in the src-file:
```html
...
<before-and-after>
    <div slot="before">
        This text is written before a static text.
    </div>
    <div slot="after">
        This text is written after a static text.
    </div>
</before-and-after>
...
```
In the component file:
```html
    <template name="before-and-after">
        <div><slot name="before"></slot></div>
        <div>This Text is in the middle</div>
        <div><slot name="after"></slot></div>
    </template>
```
Will be rendered to:
```html
...
<div>This text is written before a static text.</div>
<div>This Text is in the middle</div>
<div>This text is written after a static text.</div>
...
```

#### 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
...
<make-bold m-wrapper="h1">This is a bold header</make-bold>
...
```
will be rendered to
```html
...
<h1 style="font-weight:bold;">
    <div> 
        This is a bold header
    </div>
</h1>

<!--
Instead of 
<div style="font-weight:bold;">
    <div> 
        This is a bold header
    </div>
</div>
-->
...
```
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
<template name="red-text">
    <style>
        .textColor{color: red;}
    </style>
    <div class="textColor">This Text is red.</div>
</template>
```
Component B
```html
<template name="green-text">
    <style>
        .textColor{color: green;}
    </style>
    <div class="textColor">This Text is green.</div>
</template>
```
Src-file:
```html
...
    <red-text></red-text>
    <green-text></green-text>
...
```
This will be rendered to
```html
...
<style>
    .textColor{color: red;}
</style>
<!-- Oh no, ****. This text is green, because the text color has been overwritten in another component.-->
<div class="textColor">This Text is red.</div>

<style>
    .textColor{color: green;}
</style>
<div class="textColor">This Text is green.</div>
...
```

##### Solution
If you want scoped styles, just add a "scoped" attribute to the "style"-tag.
Component A
```html
<template name="red-text">
    <style m-scoped>
        .textColor{color: red;}
    </style>
    <div class="textColor">This Text is red.</div>
</template>
```
Component B
```html
<template name="green-text">
    <style m-scoped>
        .textColor{color: green;}
    </style>
    <div class="textColor">This Text is green.</div>
</template>
```

This will be rendered to
```html
...
<style>
    .textColor [data-v-12345]{color: red;}
</style>
<!-- Yaaay, this is red now - as expected:) -->
<div data-v-12345 class="textColor">This Text is red.</div>

<style>
    .textColor [data-v-67890]{color: green;}
</style>
<div class="textColor" data-v-67890>This Text is green.</div>
...
```

##### Efficency 
What happens if you add the same component multiple times? 
```html
..
<green-text></green-text>
<green-text></green-text>
<green-text></green-text>
...
```
Will the same styles exist multiple times?
```html
...
<!-- Will it be like this?-->
<style>
    .textColor [data-v-67890]{color: green;}
</style>
<div class="textColor" data-v-67890>This Text is green.</div>
<style>
    .textColor [data-v-67890]{color: green;}
</style>
<div class="textColor" data-v-67890>This Text is green.</div>
<style>
    .textColor [data-v-67890]{color: green;}
</style>
<div class="textColor" data-v-67890>This Text is green.</div>
...
```
No. Same styleblocks with attribute "scoped" will just exist once. the example above would look like this:
```html
...
<style>
    .textColor [data-v-67890]{color: green;}
</style>
<div class="textColor" data-v-67890>This Text is green.</div>

<div class="textColor" data-v-67890>This Text is green.</div>

<div class="textColor" data-v-67890>This Text is green.</div>
...
```

#### 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
<template name="parent-component">
    <script>
        var text = "Hello Parent";
    </script>
    <child-component></child-component>
    <script>
        console.log(text);
    </script>
</template>
```

Child-Component 
```html
<template name="child-component">
    <script>
        var text = "Hello child";
        console.log(text);
    </script>
</template>
```

This would be rendered like this:
```html
<script>
    // Declaration in the parent component
    var text = "Hello Parent";
</script>
 <script>
     // Declaration in the child component
    var text = "Hello child";
    console.log(text);
</script>
<script>
    // Oh no, the text in the parent component has been overwritten. That's not expected;
    console.log(text);
</script>
```
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
<template name="child-component">
    <script m-scoped>
        var text = "Hello child";
    </script>
</template>
```

Would be rendered to
```html
<script>
    // Declaration in the parent component
    var text = "Hello Parent";
</script>
<div id="12345" data-component="12345" data-v-12345>
    <script>
        (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";</script>"
                },
                attributes: {
                    "attributeKey1": "attributeValue1"
                    ...
                }
            };

            var text = "Hello child";
            console.log(text);
        })(window);
    </script>
</div>
<script>
    // As expected, "Hello parent" will be logged here
    console.log(text);
</script>
```

#### 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
<template name="custom-slider">
    <script src="https://a-external-provider.com/jquery-ui.min.js"></script>
    <link href="https://a-external-provider.com/jquery-ui.min.css">
    <div>
        Here we add the component elements. 
        ...
    </div>
</template>
```
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
<custom-slider></custom-slider>
...
<custom-slider></custom-slider>
```
This would be rendered to
```html
<script src="https://a-external-provider.com/jquery-ui.min.js"></script>
<link href="https://a-external-provider.com/jquery-ui.min.css">
<div>
    Here we add the component elements. 
    ...
</div>
...
<!-- Oh no, the script is loaded twice -->
<script src="https://a-external-provider.com/jquery-ui.min.js"></script>
<!-- 
    External Stylesheets are just loaded once per src-file.
    <link href="https://a-external-provider.com/jquery-ui.min.css"> 
-->
<div>
    Here we add the component elements. 
    ...
</div>
```
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
<script m-once src="https://a-external-provider.com/jquery-ui.min.js"></script>
<div>
    Here we add the component elements. 
    ...
</div>
```

Declared multiple times in a src-file it would be rendered like this:
```html
<script m-once src="https://a-external-provider.com/jquery-ui.min.js"></script>
<div>
    Here we add the component elements. 
    ...
</div>
...
<div>
    Here we add the component elements. 
    ...
</div>
```

#### Component Config
Your Component may need some specific configuration. You can add this in your component by the attribute "m-componentconfig". 
```html
<template name="any-component">
    <script m-componentconfig>
        /*Add your component configuration here*/
    </script>
</template>
```

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
<template name="any-component">
    <script m-componentconfig>
        //This data could also be fetched from anywhere else asynchronous
        const data = {
                    name: "dieter",
                    kids: [{name:'heinz'},{name:'gustav'}]
                };

        module.exports = {
            data(){
                return data;
            }
        }
    </script>
</template>
```

This data will also be rendered in the scoped scripts: 
```html
 <script>
    (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);
</script>
```
### 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:
<template name="jsrender-example">
    Hello {{:name}}.
    {{if age> 30}}
        You are reeeeally old.
    {{/if}}
    {{if age <= 30}}
        In Germany they would call you "Jungspund".
    {{/if}}
</template>

Src:
<div>
    <jsrender-example name="Peter" age="25"></jsrender-example>
</div>

Will be rendered to:
<div>
    <div>
        Hello Peter.
        In Germany the would call you "Jungspund".
    </div>
</div>
```
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