├── .editorconfig ├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── articles ├── fe2.md ├── fef.m4v └── marko.png ├── book.png ├── docs ├── .nojekyll ├── CRUD.zip ├── README.md ├── _sidebar.md ├── book.png ├── css.md ├── index.html ├── itemize.md ├── logo.png ├── lowtech.png ├── next.md ├── pug.md ├── pug.zip ├── readme.txt ├── toolBelt.md ├── ts.md ├── versions.yaml └── webs.md ├── more_examples ├── localeEg │ ├── .gitignore │ ├── includes │ │ └── header.pug │ ├── loc.yaml │ ├── pg1 │ │ ├── dat.yaml │ │ ├── en │ │ │ └── loc.pug │ │ ├── index.pug │ │ ├── m.pug │ │ ├── t-de.md │ │ ├── t-en.md │ │ └── t-ru.md │ ├── pg2 │ │ ├── dat.yaml │ │ ├── en │ │ │ └── loc.pug │ │ ├── index.pug │ │ ├── m.pug │ │ ├── readme.md │ │ ├── t-de.md │ │ ├── t-en.md │ │ └── t-ru.md │ └── redme.md └── mdFx │ ├── dat.yaml │ ├── fx.css │ ├── index.html │ ├── index.pug │ ├── mdOnly.md │ └── page.css ├── origin └── gulpfile.js ├── src ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── PugServ.js ├── PugServ.ts ├── README.md ├── lib │ ├── Base.js │ ├── Base.ts │ ├── Extra.js │ ├── Extra.ts │ ├── FileOpsBase.js │ ├── FileOpsBase.ts │ ├── Wa.js │ └── Wa.ts ├── logo.png ├── mbake.js ├── mbake.ts ├── package-lock.json ├── package.json ├── publish.sh ├── release.txt ├── tsconfig.json └── x.sh └── versions.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 3 6 | trim_trailing_whitespace = false 7 | 8 | end_of_line = lf 9 | charset = utf-8 10 | insert_final_newline = false -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* linguist-language=Pug 2 | *.* linguist-vendored=Pug 3 | 4 | *.js linguist-vendored=true 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | 4 | node_modules/ 5 | 6 | 7 | 8 | yarn.lock 9 | 10 | .yarn 11 | 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | src/CHANGELOG.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Cekvenich(https://www.INTUITION.DEV) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | src/README.md -------------------------------------------------------------------------------- /articles/fe2.md: -------------------------------------------------------------------------------- 1 | 2 | # Advanced Frontend Development for (real and) experienced developers 3 | 4 | Previously I outlined beginner backend development in article (here: https://github.com/INTUITION-dev/httpRPC/tree/master/other/doc ) 5 | And now here in part 2, I'll tackle how to effectively do Frontend development: targeting experienced developers, 7+ years of professional 6 | web development. The 7 years being based on book called: 10K hours to mastery. 7 | What I am saying to the reader is if you are a jr Frontend developer this article is not for you, ( but you can likely do beginner backend development as linked at the top - it is much easier to start and master backend - and while doing backend you can get exposed to non-imperative styles of programing, for example using SQL is declarative.) 8 | 9 | Some of the concepts I introduce may seem strange at first, a bit like eating sushi the first time. But then, you end up developing a taste for it. 10 | 11 | 12 | ### How do real developers develop UI/Frontend? 13 | 14 | When C and C++ developers need GUI the use GUI libs and some of the modern libs they use are declarative: 15 | - Qt/QML 16 | - sciter 17 | - ultralig.ht 18 | - CEF 19 | 20 | ( Some of the older C/C++ GUI lib like wx, Haxe and GTK are imperative) 21 | 22 | Above libs for C/C++ GUI are HTML / DOM based. Html is really a tree/graph of elements. 23 | 24 | ### SEO (and AMP) 25 | 26 | Some apps are secure, but others need to be viral enabled - so we optimize for SEO. Google, Bing and others offer 27 | webmaster / search console tools online - where you can check your page/screen's performance. 28 | What you have to have resized years ago: you can't use .js client side for anything SEO! 29 | 30 | And for improved mobile SEO you may need to render AMP. Again: no .js. 31 | 32 | Just HTML and CSS. 33 | You should get the idea that imperative development is mostly for backend and beginners. Imperative is not for advanced and not for Frontend. 34 | 35 | ## Real Frontend development and tooling 36 | 37 | JAMStack architecture talks about *' prerendered Markup, served without web servers '*. Nice thing about pre-rendered is: SEO :-). No need to dynamically render. No web servers also means fast, served by the edge (CDN) and infinitely scalable. ( Infinitely scalable of the Frontend; the backend APIs is a different team ). 38 | 39 | I highly recommend that all FE developers have a github page! 40 | 41 | ## Mark up 42 | 43 | eBay uses Marko for markup, eg: 44 | 45 | 46 | 47 | Nodejs Express uses the more popular Pug for Markup, and most people are familiar with it: 48 | - https://expressjs.com/en/guide/using-template-engines.html 49 | 50 | #### So how do we pre-render? 51 | 52 | You bind data at compile time for anything SEO. You can compile markup(template) with Grunt|Gulp, but after running into limitations 53 | of the build languages, it is much easier to write a build script in .js, and have full access to npm packages for your build script. You likely already know .js, but this time you are running it at compile time to generate screens/pages that will run statically. 54 | 55 | One popular tool as an alternative to a build script is Prepros.io. You can for example compile SASS to .css, Pug to html and .ts to .js. 56 | You are likely already familiar with compiling Sass to CSS as it gives you more power. The JAMstack approach does the same thing with html. 57 | 58 | With a generate for example you can reuse snipets, use same markup for SEO|AMP-no.js screen page, and an enhanced SPA version with client side .js of the same page (on first user click). 59 | 60 | 61 | #### Static / Dynamic? 62 | 63 | An example JAMstack is both static and dynamic: show a story static without need for .js! Then progressively enhance to display comments dynamically and improve UX. 64 | 65 | 66 | ## Cross Platform: Electron 67 | 68 | Responsive approach allow us to develop a web app that works on desktop and mobile screens. 69 | One important tool to master is Electron. It allows a path to cross platform development. 70 | 71 | For example: IOS and Android running the same exact code base that is running your Web App. 72 | But it should be done in two steps: 73 | 1. Port your app to Electron 74 | 2. Port the Electron app to https://capacitor.ionicframework.com/docs/getting-started. 75 | 76 | 77 | ### Custom Elements 78 | 79 | Lets write a standard (built into browser with no 3rd party libs) custom element: 80 | 81 | ``` 82 | var cTemp = document.createElement('template') 83 | cTemp.innerHTML = ` 84 | 86 | 87 | I'm a Cust. El 88 | ` 89 | customElements.define('c-custel', class extends HTMLElement { 90 | sr 91 | constructor() { 92 | super() 93 | 94 | this.sr = this.attachShadow({ mode: 'open' }) 95 | this.sr.appendChild(cTemp.content.cloneNode(true)) 96 | }//cons 97 | 98 | })//define 99 | ``` 100 | 101 | That code creates a new dom element: c-custel without any 3rd party libs or packs. 102 | 103 | The purpose of writing imperative (.js) is to: add a new DOM element c-custel. 104 | The purpose is: to reduce .js code! And increase DOM code. 105 | Make sure you have purpose in mind. 106 | 107 | The nice thing about using standard custom elements is that you can use them with any .js framework|library (eg: Vue single file components), with typescript. 108 | And you can deliver in several shareable bundles to avoid downloading a large monolithic bundle. 109 | 110 | 111 | ### Lab: Non-imperative 112 | 113 | To get the feel for non-imperative programing you should write a hello world levering in Mavo.io. 114 | Mavo is developed by Lea Verou who is on the HTML standards committee. 115 | 116 | Note: I'm not recommending or using Mavo. I am saying it is a great learning tool that takes less than an hour so you can feel an important concept. 117 | 118 | Instead of html, use Pug (eg: Prepros.io). 119 | 120 | 1. A hello world Mavo in Pug, the simpler the better (instructor provided) 121 | 2. Port to Electron (instructor provided) 122 | 3. Port to https://capacitor.ionicframework.com/docs/getting-started 123 | 124 | Email the app file produced by https://capacitor.ionicframework.com/docs/getting-started to your phone. 125 | 126 | 127 | ## Frontend (CSS) frameworks, and naming. 128 | 129 | Here is a video showing some results when you google for Frontend frameworks: 130 | 131 | 133 | 134 | So to be clear, we should delineate Frontend CSS frameworks that include .js and Frontend .js only frameworks. 135 | 136 | Senior Frontend Developer should have experience with a more than one Frontend CSS frameworks as well as migrating from one to another. 137 | 138 | Note: above all, a Senior Frontend developers has years of experience and depp knowledge of CSS. 139 | 140 | Note: there are two things hard in CS. Caching(backend) and naming. Naming DOM and 'classes' is hard. 141 | One bad thing is to use inline styles, for example the 'atomic' approach. 142 | 143 | That is the main challenge for Sr. Frontend developers: naming. The names have to be socialized and rehashed from time to time. 144 | One thing that might 145 | It's not just rapid development of front end prototypes - the aim is maintainable naming. 146 | 147 | ### Mobile org chart 148 | 149 | Obviously having a separate mobile team doing Android and IOs will obstruct cross platform productivity: DRY. 150 | 151 | Instead the frontend team's teach leads are broken up by the users persona, the people your org is targeting in helping. 152 | 153 | (aside: each frontend team should have a single member from the backend team that is writing the client side apis for them - APIs that map to the ViewModel) 154 | 155 | Some call the Senior Frontend developers unicorns: designers that code. That is one of the reasons that the frontend is a higher salary band than backend. 156 | Also due to constraints, Senior Frontend developers maybe remote or at least have a Friday WAH (work from home), (vs backend that likely have to be local due to security of backend data). 157 | 158 | Candidates Senior Frontend developers should have some example sites, including their own, and opinion on css websites they like and other other sr Frontend developers they admire. (note: If the people they admire are .js or imperative developers I myself would consider them for the backend team) 159 | And they must demonstrate a liking for CSS. 160 | 161 | The Frontend developers tend to be more senior than backend: and should have potential to become principal Developers, designers or product managers once they advance. 162 | 163 | One should also know how long it takes to build an average CRUD page. 164 | 165 | ### Low-code future 166 | 167 | There are also low-code tools that are coming up in the future, something to keep an eye one. 168 | 169 | ## Summary: 170 | 171 | Sr Frontend developers must have deep expertise in CSS, SEO and know cross platform development. 172 | 173 | Skills include: 174 | - Markup, declarative and CSS 175 | - Good 'class' naming is the bane of front end development 176 | - Mobile is cross platform 177 | - Custom elements can be used with any .js lib, it is built into the browser 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /articles/fef.m4v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/articles/fef.m4v -------------------------------------------------------------------------------- /articles/marko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/articles/marko.png -------------------------------------------------------------------------------- /book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/book.png -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CRUD.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/docs/CRUD.zip -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | # mbake CLI 3 | 4 | ## 'All my friends KNOW the low-coder' 5 | 6 | #### INTUITION is open source and extensible low-code productivity bundler/builder for front-end developers. 7 | 8 | ## Overview 9 | 10 | MetaBake(mbake) provides a bundler/builder for a developer to write cleaner code quicker, with `pug` and livereload out of the box. 11 | 12 | You can gradually adopt it while it allows you to develop quicker - and with clean simplicity - Static Websites, Custom Elements, custom CMS/E-Commerce, CRUD and all sorts of dynamic Web Apps. 13 | 14 | ### Prerequisites 15 | 16 | You should know HTML, CSS and JavaScript - that is all we use. If you need to catch up, we recommend this book: 'Design and Build Websites' by Jon Duckett. 17 | 18 | 19 | ## Quick start 20 | 21 | 22 | ```sh 23 | npm i -g mbake 24 | mbake -w . /* for a base website */ 25 | ``` 26 | 27 | 28 | ## INTUITION in 4 Minutes 29 | 30 | Building sites take a few moments, just add `index.pug` and `dat.yaml` files in the folder, and compile it with `mbake .` from the root folder of your site. 31 | 32 | 33 | ### Example 34 | Create a folder called 'one'. 35 | In the folder 'one', create file index.pug 36 | 37 | ```pug 38 | 39 | header 40 | body 41 | p Hello #{key1} 42 | 43 | ``` 44 | 45 | and create file dat.yaml 46 | ```yaml 47 | key1: World 48 | ``` 49 | > Note: to create a new page|screen in mbake, create a new folder with an index.pug and day.yaml in each folder. 50 | 51 | 52 | ### Now make with mbake: 53 | 54 | ```sh 55 | mbake . 56 | ``` 57 | 58 | This will create `index.html`. Of course you can use regular Pug syntax to include other Pug files or Markdown. (INTUITION Markdown flavor includes CSS support): 59 | ```pug 60 | body 61 | div 62 | include:metaMD comment.md 63 | ``` 64 | 65 | And example Markdown file with CSS nested classes. Title is nested in 2 levels, .column class CSS and second level .stick CSS class 66 | ``` 67 | :::: column col-2 68 | ::: stick 69 | Title 70 | ::: 71 | :::: 72 | 73 | ``` 74 | 75 | 76 | 77 | So if you write a Markdown file comment.md, it will be included in index.html 78 | --- 79 | 80 | 81 | ### Watcher 82 | 83 | This will start a webserver and auto-refresh browser, and watch for file changes to auto build: 84 | ```sh 85 | mbakeX -w . 86 | ``` 87 | 88 | Instead of `.` you can specify any path. 89 | Also, the fact that we are generating this static content allows us to have the entire Web App served by a CDN 90 | 91 | 92 | ## SASS 93 | CSS can be hard to work with, so people use Sass/Scss. Create a `style.scss` file: 94 | ```css 95 | $font-stack: Helvetica, sans-serif; 96 | $primary-color: #333; 97 | 98 | body { 99 | font: 100% $font-stack; 100 | color: $primary-color; 101 | } 102 | ``` 103 | Create file `assets.yaml` in assets folder, to compile your scss to css 104 | 105 | ``` 106 | css: 107 | - style.scss 108 | ``` 109 | 110 | and run 111 | ```sh 112 | mbake -s . 113 | ``` 114 | 115 | It will create a css file in `assets/css` with auto-prefixes. 116 | 117 | So the structure of asset folder should look something like that: 118 | ```folder 119 | assets/ 120 | css/style.css /* this is going to be compiled from style.scss */ 121 | scss/style.scss /* your working area */ 122 | assets.yaml /* with `scss` files that need to be compiled */ 123 | ... 124 | ``` 125 | 126 | ## TypeScript 127 | 128 | TypeScript is supper-set of JavaScript. Write a ts file, like foo.ts: 129 | ```ts 130 | foo(i:number) { 131 | console.log('oh hi') 132 | } 133 | ``` 134 | 135 | and run 136 | ```sh 137 | mbake -t . 138 | ``` 139 | 140 | Lots of time you use .ts to call DB services: such as Google FireStore. 141 | 142 | ## Examples - Website 143 | 144 | There are 12 very different examples included in the mbake CLI. One is just a website: 145 | ```sh 146 | mbake -w 147 | ``` 148 | 149 | That will extract an example website in the current folder. ( Obviously you can create any layout with any combination of css and other libraries, but here is how we laid out an example/starter website). 150 | 151 | 152 | 153 | ## CMS/Itemize example 154 | 155 | ### Itemize (eg CMS) 156 | 157 | Lets build a folder called `Items` and in that folder create a blank file `dat_i.yaml,` with nothing there. 158 | - In the folder called `Items` create folder `Page1` and folder `Page2`. In each page folder create `index.pug` and `dat.yaml`. So you have `Page1` and `Page2` folder under `Items`. 159 | 160 | - In each Page's `dat.yaml` add 161 | ``` 162 | title: Page name 163 | ``` 164 | And add a few more key/value words in each dat.yaml, but make each pages values a bit different. 165 | 166 | - And now, in the folder `Items` run 167 | 168 | ```sh 169 | mbake -i . 170 | ``` 171 | It will create `items.json`. This allows you to fetch that json and search for content, CMS, items, etc. 172 | 173 | ## INTUITION FrameWork(FW)/Application Architecture(AA) 174 | 175 | There is not much to ours. 176 | 177 | ```sh 178 | mbake -f . 179 | ``` 180 | 181 | This emits a Pug file that you should include in your Pug's layout head section. 182 | In turn, the included file calls a 'setup-deffs' js file that defines and show you how to define things you can require later. 183 | 184 | 185 | ## Next steps 186 | 187 | Now that you know mbake foundation, here are some choices for next things to learn in the advanced docs, pick and chose: 188 | 189 | - CMS: an admin panel that you can host to can use as is; or as a base to build commercial grade CMS or eCommerce site, including browser plugin. 190 | - MetaCake: plugin components, makes it easy for designers to write real Web Apps. Developed with RIOTjs, easier than Reactjs (commercial license optional) 191 | - AMP 192 | - SPA router: with page transition effects and state machine (needed for cross-platform development) 193 | - Cross platform development with real single code base development: single code base for Web, AMP, Electron and Capacitor (by Ionic) 194 | 195 | - VS code from the Cloud: multiple developers using a browser against same VS Code host in the cloud 196 | 197 | **Other examples include:** 198 | 199 | - Using markdown CSS effect: allows non-programmers to write interactive stories 200 | - Slide show with markdown 201 | - Dashboard example 202 | - Ads example 203 | 204 | 205 | # Links 206 | 207 | - Full Docs: [docs.INTUITION.DEV](http://docs.INTUITION.DEV) 208 | - [INTUITION.DEV](https://www.INTUITION.DEV) 209 | - [blog.MetaBake.net](http://blog.MetaBake.net) 210 | - [Github](http://git.INTUITION.DEV) 211 | - Check for the latest version of mbake: [npm.js](http://npmjs.com/package/mbake) 212 | -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | 2 | * [MetaBake](README.md) 3 | * [HTML/Pug and dat.yaml](pug.md) 4 | * [SCSS|CSS](css.md) 5 | * [Website w/ loading .js libs example](webs.md) 6 | * [TypeScript|.js](ts.md) 7 | 8 | * [CMS/Itemize example](itemize.md) 9 | 10 | * [ToolBelt (of js libs)](mbToolBelt.md) 11 | * [Review, extras and Next steps](next.md) 12 | -------------------------------------------------------------------------------- /docs/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/docs/book.png -------------------------------------------------------------------------------- /docs/css.md: -------------------------------------------------------------------------------- 1 | # SASS 2 | 3 | CSS can be hard to work with so people use Sass/Scss. Create a ex.sass file: 4 | ```css 5 | $font-stack: Helvetica, sans-serif; 6 | $primary-color: #333; 7 | 8 | body { 9 | font: 100% $font-stack; 10 | color: $primary-color; 11 | } 12 | ``` 13 | 14 | Create a files assets.yaml 15 | ``` 16 | css: 17 | - ex.sass 18 | ``` 19 | 20 | and run 21 | ```sh 22 | mbake -s . 23 | ``` 24 | It will create a css file with auto-prefixes. 25 | 26 | 27 | ### More on Sass 28 | In general you should leverage a CSS framework, there are many to chose from. 29 | 30 | For font, we default to Open Sans, but like a CSS framework, you can use any. 31 | 32 | Also, as mentioned before INTUITION Markdown supports css classes. -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/itemize.md: -------------------------------------------------------------------------------- 1 | 2 | # CMS/Itemize example 3 | 4 | ### Itemize (eg CMS) 5 | 6 | 1. Lets build a folder called Items and in that folder create 7 | create a blank file dat_i.yaml, with nothing there. 8 | 9 | 2. In the folder called Items create folder Page1 and folder Page2. 10 | In each page folder create index.pug and dat.yaml. 11 | So you have Page1 and Page2 folder under items 12 | 13 | 3. In each Page's dat.yaml add 14 | ``` 15 | title: Page name 16 | ``` 17 | And add a few more key/value words in each dat.yaml, but make each pages values a bit different. 18 | 19 | 4. And now, in the folder Items run 20 | ```sh 21 | mbake -i . 22 | ``` 23 | It will create items.json. 24 | This allows you to fetch that json; and search for content, CMS, items, etc. 25 | 26 | ### mbake -b will emit an example CMS with above. 27 | 28 | The example CMS will also show you how the items.json is read to display a nice searchable and paginated 29 | list of items. No magic. 30 | 31 | --- -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/docs/logo.png -------------------------------------------------------------------------------- /docs/lowtech.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/docs/lowtech.png -------------------------------------------------------------------------------- /docs/next.md: -------------------------------------------------------------------------------- 1 | 2 | # mbake review, extras and next steps 3 | #### 'All my friends KNOW the low-coder' 4 | 5 | mbake is the open source modern tool for developers that makes you most productive: it makes Pug, SCSS and Typescript. 6 | It comes with example apps that include WebSite showing how to load.js, CRUD showing how to do dynamic apps. You can use any 7 | application architecture you like, but 8 | Also it you you can itemize (-i dat.yaml files) to make a nice SEO friendly CMS. 9 | 10 | ## Next steps 11 | 12 | Now that you know mbake foundation, here are some choices for next things to learn in the advanced docs, pick and chose: 13 | 14 | - CMS: an admin panel that you can host to can use as is; or as a base to build commercial grade CMS or eCommerce site, including browser plugin. 15 | - MetaCake: plugin components, makes it easy for designers to write real Web Apps. Developed with RIOTjs, easier than Reactjs (commercial license optional) 16 | - AMP 17 | - SPA router: with page transition effects and state machine (needed for cross-platform development) 18 | - Cross platform development with real single code base development: single code base for Web, AMP, Electron and Capacitor (by Ionic) 19 | 20 | - VS code from the Cloud: multiple developers using a browser against same VS Code host in the cloud 21 | 22 | Other examples include: 23 | - Using markdown CSS effect: allows non-programmers to write interactive stories 24 | - Slide show with markdown 25 | - Dashboard example 26 | - Ads example 27 | 28 | --- 29 | 30 | 31 | More: 32 | - Online support and (optionally commercial) training 33 | - PWA 34 | - Federated CMS (commercial) 35 | - Consulting: audit software development productivity best practices 36 | - Commercial development by sponsor: www.INTUITION .net - 10X cheaper and quicker 37 | 38 | 39 | ## Awesome list of low-code and INTUITION resources: 40 | 41 | - https://github.com/MetaBake/mbakeDocs/tree/master/awesomeReference -------------------------------------------------------------------------------- /docs/pug.md: -------------------------------------------------------------------------------- 1 | 2 | # Pug Markup 3 | 4 | We use Pug instead of html: 5 | ```pug 6 | body 7 | h1 Pug - node template engine 8 | //- a comment in Puf 9 | #container.col 10 | p You are amazing 11 | ``` 12 | instead of: 13 | ```html 14 | 15 |

Jade - node template engine

16 |
17 |

You are amazing

18 |
19 | 20 | ``` 21 | So if you know html, you know Pug! 22 | If you need more of an intro to Pug [Pug (aka Jade) on Youtube](http://youtube.com/watch?v=wzAWI9h3q18) 23 | 24 | 25 | In addition you can include html parts as needed: 26 | ```pug 27 | include includes/head 28 | ``` 29 | 30 | Also you extend a layout, you can see that in the example apps. 31 | 32 | And there are online converters where you type in html and it gives you pug equivalent. (eg: http://pug.INTUITION.DEV) 33 | 34 | And that is all there is about Pug. 35 | 36 | 37 | ### To make/build 38 | ```sh 39 | mbake . 40 | ``` 41 | where . is the current directory to build, or can be a path. If there is no index.pug and dat.yaml it will not build. 42 | 43 | ### dat.yaml 44 | In order to 'make' index.html from an index.pug, it needs a dat.yaml in the same folder, eg: 45 | 46 | ```yaml 47 | key1: World 48 | title: My page about puppies 49 | #special words: 50 | basedir: ../../ 51 | pretty: false 52 | ``` 53 | 54 | 55 | You can use any name/vale, and then use in Pug like: 56 | ```pug 57 | p Hello #{key1} 58 | ``` 59 | for example with page Title that may need to be a h1 as well. This is often used for SEO tags, for example Twitter and Linkedin in tags often have same keyword text. 60 | 61 | There are some special words like: pretty. If pretty: true, it will make a nice looking htlm, it won't be compressed. 62 | 63 | basedir keywords sets the base directory to look for includes, so in pug you can say / 64 | / is the base dir. If you move the page folder, you can then just change your basedir in dat.yaml. 65 | 66 | ## Markdown 67 | 68 | ```pug 69 | include:metaMD comment.md 70 | ``` 71 | 72 | And example Markdown file with CSS nested classes. Title is nested in 2 levels, .column class CSS and second level .stick CSS class 73 | ``` 74 | :::: column col-2 75 | ::: stick 76 | Title 77 | ::: 78 | :::: 79 | 80 | ``` 81 | 82 | This lets you include Markdown in Pug. Markdown is great for people that are not technical to generate content. 83 | 84 | ( eg: https://github.com/markdown-it/markdown-it-container, via Markdown-it ) 85 | 86 | 87 | ## Folder Structure 88 | 89 | In examples you can see typically application page structure: 90 | 91 | ``` 92 | /pages/page1 93 | /pages/page2 94 | /pages/page3 95 | /layouts 96 | /includes 97 | /assets 98 | ``` 99 | 100 | Each page should have all its own assets in its folder. This avoids 'digital rot' where a page stops working as its assets are misplaced. 101 | The other support folders like /layouts, /includes, and /assets are used only if something is needed in more than one page. 102 | 103 | 104 | #### In a 'header.pug' or similar file you can include 'a href' to link to other pages. There are other ways to link to pages in advanced sections. 105 | 106 | --- 107 | 108 | ## Watcher 109 | 110 | This will start a webserver and auto-refresh browser, and watch for file changes to auto build: 111 | 112 | ```sh 113 | mbakeX -w . 114 | ``` 115 | 116 | Instead of . you can specify any path. 117 | Also, the fact that we are generating this static content allows us to have the entire Web App served by a CDN. 118 | 119 | --- 120 | 121 | ## Re: Build Tools Gulp/Grunt 122 | 123 | You can also transpile Pug with other build tools like Gulp/Grunt (or even prepros.io) using their syntax. mbake CLI is written in .js. 124 | This allows us to use the latest features of the needed npm libraries. And allows you to extend our classes to write your custom version of mbake CLI - explained in the advanced sections. At least INTUITION should inspire you to use generators. 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/pug.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/docs/pug.zip -------------------------------------------------------------------------------- /docs/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | http://docsify.js.org/#/plugins 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/toolBelt.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # MetaBake's ToolBelt 4 | 5 | Listed dependencies gives you application a list of a election curated js libraries that you may chose to use/require. 6 | 7 | - Docs: https://MetaBake.github.io/mbakeDocs/#/mbToolBelt 8 | 9 | It saves time, sets baseline for depps, defines popular/approved libs: 10 | 11 | mbake -f . 12 | 13 | 14 | 15 | It also contains polyfill and some other frequently needed functions. 16 | 17 | 18 | ## Require Depps 19 | 20 | The tool belt is built on top of: https://github.com/muicss/johnnydepp 21 | 22 | The listed dependencies are just the popular/approved ones, in your apps 'load.js' you can define what you want to use. And then simply require it. 23 | And/or: you don't have to use most of them, or even any of them. But it does demonstrate a base line of how to setup a 24 | 25 | The tool belt also give a bit of application architecture, a baseline on how things work together, helps manage 'dll hell' 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/ts.md: -------------------------------------------------------------------------------- 1 | 2 | ## TypeScript 3 | 4 | TypeScript is supper-set of JavaScript. Write a ts file, like foo.ts: 5 | ```ts 6 | foo(i:number) { 7 | console.log('oh hi') 8 | } 9 | ``` 10 | and run 11 | ```sh 12 | mbake -t . 13 | ``` 14 | 15 | --- 16 | (of course you have to load any polyfills like 17 | promises, fetch or what you need) 18 | 19 | And normally you run that command at the root of your Web App; but don't use it for back-end node.js. 20 | -------------------------------------------------------------------------------- /docs/versions.yaml: -------------------------------------------------------------------------------- 1 | 2 | agentg: v2.5.1 3 | 4 | mbake: v8.5.2 5 | 6 | mbakex: v8.5.2 7 | 8 | 9 | 10 | headFrag: https://cdn.jsdelivr.net/gh/intuition-dev/mbToolBelt@v8.4.25/src/template/headFrag.pug 11 | 12 | pug: https://intuition-dev.github.io/mbCLI/pug.zip 13 | CRUD: https://intuition-dev.github.io/mbCLI/CRUD.zip 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/webs.md: -------------------------------------------------------------------------------- 1 | 2 | ## Examples - Website 3 | 4 | There are 12 very different examples included in the mbake CLI. One is just a website: 5 | 6 | ```sh 7 | mbake -w 8 | ``` 9 | That will extract an example website in the current folder. ( Obviously you can create any layout with any combination of css and other libraries, but here is how we laid out an example/starter website). 10 | 11 | --- 12 | It has a README.md in root of the website. 13 | 14 | 15 | You'll find the real loading code in /assets/js/loader.js. 16 | 17 | You should review the standard layout of folders: 18 | - /layouts has the main Pug layouts that each page extends. 19 | - /landing/* has the website pages, the index.pug and dat.yaml. To navigate to a webpage is a 'a href' to the 20 | /landing/page1 or landing/page2. 21 | - /assets/assets.yaml to process the Sass into css 22 | 23 | There are many navigation libraries on github - a way to create a navbar, and we leverage one in here. In advanced docs we review some nav libs, but not here. 24 | 25 | And as mentioned at the start: there are 12 example of very different Web Apps, you should study a few to see how we chose to layout things so you get your own ideas. 26 | 27 | --- 28 | 29 | (Notice there is also a loader.min.js. mbake -t created the min file, even when .ts is not found it semi minimizes .js. 30 | It does not do a full min so that in production we can have reasonable error messages about our code. ) 31 | 32 | -------------------------------------------------------------------------------- /more_examples/localeEg/.gitignore: -------------------------------------------------------------------------------- 1 | *.html -------------------------------------------------------------------------------- /more_examples/localeEg/includes/header.pug: -------------------------------------------------------------------------------- 1 | header 2 | nav 3 | a(href='#') you site name 4 | div.nav-item 5 | a(href='/pg1/'+LOCALE, data-id="lang").links Page 1 6 | a(href='/pg2/'+LOCALE, data-id="lang").links Page 2 7 | form(name="select_language") 8 | select(name="lang") 9 | option(value='en') EN 10 | option(value='ru') RU 11 | option(value='de') DE 12 | script. 13 | var form = document.forms[0]; 14 | var select = form.elements.lang; 15 | 16 | var lang = window.sessionStorage.getItem('lang'); 17 | 18 | if(lang != null){ 19 | select.value = lang 20 | }else { 21 | select.value = 'en' //default value for language 22 | } 23 | 24 | select.onchange = function(event){ 25 | var option = event.target.selectedOptions[0] 26 | select.value = option.value 27 | 28 | window.sessionStorage.setItem('lang', option.value); 29 | 30 | //get all .links, and replace its language 31 | var links = document.querySelectorAll('a.links') 32 | 33 | for(var i=0; i< links.length; i++){ 34 | links[i].href = links[i].href.slice(0,-2) + option.value 35 | } 36 | } 37 | 38 | style. 39 | nav { 40 | display: flex; 41 | justify-content: space-between; 42 | } 43 | .nav-item { 44 | display: flex; 45 | } 46 | .nav-item a { 47 | margin-right: 16px; 48 | } -------------------------------------------------------------------------------- /more_examples/localeEg/loc.yaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | # list of locales 4 | loc: 5 | - en 6 | - ru 7 | - de 8 | 9 | # default 10 | key1: hello 11 | 12 | key1-ru: Здравствуй 13 | key1-de: 'Herzlich wilkommen' -------------------------------------------------------------------------------- /more_examples/localeEg/pg1/dat.yaml: -------------------------------------------------------------------------------- 1 | 2 | LOC: .. -------------------------------------------------------------------------------- /more_examples/localeEg/pg1/en/loc.pug: -------------------------------------------------------------------------------- 1 | head 2 | 3 | body 4 | include ../../includes/header 5 | p OK #{key1} 6 | span 7 | img(src='https://cdn-images-1.medium.com/max/2400/0*o0-6o1W1DKmI5LbX.png', alt="en", width='20px', height='10px') 8 | include:metaMD ../t-en.md -------------------------------------------------------------------------------- /more_examples/localeEg/pg1/index.pug: -------------------------------------------------------------------------------- 1 | head 2 | 3 | body 4 | include ../includes/header 5 | 6 | if LOCALE === 'en' 7 | h1 Locale is 8 | span.en 9 | | #{LOCALE} 10 | span 11 | img(src='https://cdn-images-1.medium.com/max/2400/0*o0-6o1W1DKmI5LbX.png', alt="en", width='20px', height='10px') 12 | include:metaMD t-en.md 13 | 14 | else if LOCALE === 'ru' 15 | h1 Locale is 16 | span.ru 17 | | #{LOCALE} 18 | span 19 | img(src='https://upload.wikimedia.org/wikipedia/en/archive/f/f3/20120812153730%21Flag_of_Russia.svg', alt="ru", width='20px', height='10px') 20 | include:metaMD t-ru.md 21 | 22 | else if LOCALE === 'de' 23 | h1 Locale is 24 | span.de 25 | | #{LOCALE} 26 | span 27 | img(src='https://upload.wikimedia.org/wikipedia/en/thumb/b/ba/Flag_of_Germany.svg/1280px-Flag_of_Germany.svg.png', alt="de", width='20px', height='10px') 28 | include:metaMD t-de.md 29 | 30 | hr 31 | 32 | p Hello, #{key1}, page 1 -------------------------------------------------------------------------------- /more_examples/localeEg/pg1/m.pug: -------------------------------------------------------------------------------- 1 | head 2 | 3 | body 4 | p mobile #{key1} -------------------------------------------------------------------------------- /more_examples/localeEg/pg1/t-de.md: -------------------------------------------------------------------------------- 1 | Hallo, grüß dich -------------------------------------------------------------------------------- /more_examples/localeEg/pg1/t-en.md: -------------------------------------------------------------------------------- 1 | oh hi -------------------------------------------------------------------------------- /more_examples/localeEg/pg1/t-ru.md: -------------------------------------------------------------------------------- 1 | Привет -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/dat.yaml: -------------------------------------------------------------------------------- 1 | 2 | LOC: .. -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/en/loc.pug: -------------------------------------------------------------------------------- 1 | head 2 | 3 | body 4 | include ../../includes/header 5 | p OK #{key1} 6 | span 7 | img(src='https://cdn-images-1.medium.com/max/2400/0*o0-6o1W1DKmI5LbX.png', alt="en", width='20px', height='10px') 8 | include:metaMD ../t-en.md -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/index.pug: -------------------------------------------------------------------------------- 1 | head 2 | 3 | body 4 | include ../includes/header 5 | 6 | if LOCALE === 'en' 7 | h1 Locale is 8 | span.en 9 | | #{LOCALE} 10 | span 11 | img(src='https://cdn-images-1.medium.com/max/2400/0*o0-6o1W1DKmI5LbX.png', alt="en", width='20px', height='10px') 12 | include:metaMD t-en.md 13 | 14 | else if LOCALE === 'ru' 15 | h1 Locale is 16 | span.ru 17 | | #{LOCALE} 18 | span 19 | img(src='https://upload.wikimedia.org/wikipedia/en/archive/f/f3/20120812153730%21Flag_of_Russia.svg', alt="ru", width='20px', height='10px') 20 | include:metaMD t-ru.md 21 | 22 | else if LOCALE === 'de' 23 | h1 Locale is 24 | span.de 25 | | #{LOCALE} 26 | span 27 | img(src='https://upload.wikimedia.org/wikipedia/en/thumb/b/ba/Flag_of_Germany.svg/1280px-Flag_of_Germany.svg.png', alt="de", width='20px', height='10px') 28 | include:metaMD t-de.md 29 | 30 | hr 31 | 32 | p Hello, #{key1}, page 2 -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/m.pug: -------------------------------------------------------------------------------- 1 | head 2 | 3 | body 4 | p mobile #{key1} -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/readme.md: -------------------------------------------------------------------------------- 1 | # How to localize 2 | -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/t-de.md: -------------------------------------------------------------------------------- 1 | Hallo, grüß dich -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/t-en.md: -------------------------------------------------------------------------------- 1 | oh hi -------------------------------------------------------------------------------- /more_examples/localeEg/pg2/t-ru.md: -------------------------------------------------------------------------------- 1 | Привет -------------------------------------------------------------------------------- /more_examples/localeEg/redme.md: -------------------------------------------------------------------------------- 1 | # How to localize 2 | 3 | 1. In the root of this example there is `loc.yaml` file that has the 'dictionary' with a list of languages: 4 | 5 | ```conf 6 | # list of locales 7 | loc: 8 | - en 9 | - ru 10 | - de 11 | ``` 12 | 13 | This would be your default language or if there is no translation: 14 | 15 | ```conf 16 | # default 17 | key1: hello 18 | ``` 19 | 20 | And then you need to define each language and each key. 21 | Each key should have a postfix whith the language (eg: `-ru` for Russian). Else it uses default: 22 | 23 | ```conf 24 | key1-ru: Здравствуй 25 | key1-de: 'Herzlich wilkommen' 26 | ``` 27 | 28 | Where `-ru` is for Russian, `-de` is for German 29 | 30 | 2. The page needs to say in `dat.yaml` where the loc.yaml is with the variable called `LOC`, inspect `/pg1/dat.yaml`: 31 | 32 | ```conf 33 | LOC: .. 34 | ``` 35 | 36 | 3. The pug will in scope have a variable `LOCALE` that says what locale is the page in. Eg: ru, or es, or en, or hr. 37 | So in `/pg1/index.pug` there is 'if' logic to display `.md` file according to the language of the page: 38 | 39 | ```html 40 | if LOCALE === 'en' 41 | h1 Locale is 42 | span.en 43 | | #{LOCALE} 44 | span 45 | img(src='https://cdn-images-1.medium.com/max/2400/0*o0-6o1W1DKmI5LbX.png', alt="en", width='20px', height='10px') 46 | include:metaMD t-en.md 47 | 48 | else if LOCALE === 'ru' 49 | h1 Locale is 50 | span.ru 51 | | #{LOCALE} 52 | span 53 | img(src='https://upload.wikimedia.org/wikipedia/en/archive/f/f3/20120812153730%21Flag_of_Russia.svg', alt="ru", width='20px', height='10px') 54 | include:metaMD t-ru.md 55 | 56 | else if LOCALE === 'de' 57 | h1 Locale is 58 | span.de 59 | | #{LOCALE} 60 | span 61 | img(src='https://upload.wikimedia.org/wikipedia/en/thumb/b/ba/Flag_of_Germany.svg/1280px-Flag_of_Germany.svg.png', alt="de", width='20px', height='10px') 62 | include:metaMD t-de.md 63 | ``` 64 | 65 | 4. When you the command: 66 | 67 | ```conf 68 | $ mbake . 69 | ``` 70 | it will generate for each page an ./en, ./ru, ./de pages with the proper values, accordingly for all languages specified in the 'dictionary' in `loc.yaml`: 71 | 72 | ```conf 73 | # list of locales 74 | loc: 75 | - en 76 | - ru 77 | - de 78 | ``` 79 | 80 | 5. If some page needs a different layout by locale, create a `loc.pug` file in the locale page folder, eg: `/pg1/en/loc.pug`. 81 | It will then ignore index.pug above and will use different layout just for `/en/` page from the file `/pg1/en/loc.pug`: 82 | 83 | ```conf 84 | head 85 | 86 | body 87 | p OK #{key1} 88 | span 89 | img(src='https://cdn-images-1.medium.com/max/2400/0*o0-6o1W1DKmI5LbX.png', alt="en", width='20px', height='10px') 90 | include:metaMD ../t-en.md 91 | ``` 92 | 93 | this option is very useful when in some language there is different word's length and you need to use different design for the words to fit into the page. 94 | 95 | 6. AMP is also localized if you have `m.pug` file for AMP in the page folder with `dat.yaml`, eg /pg1//m.pug: 96 | 97 | ```conf 98 | head 99 | 100 | body 101 | p mobile #{key1} 102 | ``` -------------------------------------------------------------------------------- /more_examples/mdFx/dat.yaml: -------------------------------------------------------------------------------- 1 | pretty: false -------------------------------------------------------------------------------- /more_examples/mdFx/fx.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | http://alligator.io/css/position-sticky 5 | http://www.w3schools.com/howto/howto_css_parallax.asp 6 | */ 7 | 8 | 9 | .stick { 10 | position: sticky; /* sticky */ 11 | top: 10px; 12 | } 13 | 14 | .fix1, .fix2 { 15 | 16 | height: 200px; 17 | 18 | background-position: center center; 19 | 20 | background-attachment: fixed; 21 | background-repeat: no-repeat; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /more_examples/mdFx/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 |
Title 1
12 |
13 |
14 |

CAontrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.

15 |
16 |

17 | CBontrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. 18 | 19 |

20 |
21 |
xxx
22 |
23 |
24 | 25 |
26 |
27 |

Title

28 |
29 |
30 | 31 |

CContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet…”, comes from a line in section 1.10.32.

32 | 33 |
34 |

CDontrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet…”, comes from a line in section 1.10.32.

35 |
36 | 37 |

3

38 |
39 |
40 | 41 |
42 | 43 |
44 |
45 |

Title

46 |
47 |
48 | 49 |

CContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet…”, comes from a line in section 1.10.32.

50 | 51 |
52 |

CDontrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet…”, comes from a line in section 1.10.32.

53 |
54 | 55 |

3

56 |
57 |
58 | 59 |
60 | -------------------------------------------------------------------------------- /more_examples/mdFx/index.pug: -------------------------------------------------------------------------------- 1 | head 2 | meta(name="viewport", content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover") 3 | 4 | //- don't use remote css, manage it 5 | 6 | 7 | link(href='fx.css', rel='stylesheet', type='text/css') 8 | 9 | body 10 | link(href='page.css', rel='stylesheet', type='text/css') 11 | 12 | .container 13 | //- this is pug 'row' 14 | .columns 15 | .column.col-2 16 | .stick Title 1 17 | 18 | .column.col-9 19 | p. 20 | CAontrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. 21 | .fix1 22 | p. 23 | CBontrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. 24 | 25 | .column.col-1 xxx 26 | hr 27 | // same 'row' in pure markdown 28 | include:metaMD mdOnly.md 29 | hr 30 | // same 'row' in pure markdown again 31 | include:metaMD mdOnly.md 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /more_examples/mdFx/mdOnly.md: -------------------------------------------------------------------------------- 1 | ::::: columns 2 | 3 | :::: column col-2 4 | ::: stick 5 | Title 6 | ::: 7 | :::: 8 | 9 | :::: column col-9 10 | CContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. 11 | ::: fix2 12 | ::: 13 | CDontrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. 14 | :::: 15 | 16 | ::: column col-1 17 | 3 18 | ::: 19 | 20 | ::::: -------------------------------------------------------------------------------- /more_examples/mdFx/page.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .fix1, .fix2 { 4 | background-image: url('logo.jpg'); 5 | } -------------------------------------------------------------------------------- /origin/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var less = require('gulp-less') 3 | var LessAutoprefix = require('less-plugin-autoprefix') 4 | var autoprefix = new LessAutoprefix( { browsers: ['last 2 versions'] } ) 5 | var debug = require('gulp-debug') 6 | 7 | var ts = require('gulp-typescript') 8 | var pug = require('gulp-pug') 9 | var riot = require('gulp-riot') 10 | var rename = require('gulp-rename') 11 | gulp.task('default', defaultTask) 12 | function defaultTask(done) { 13 | gulp.start('css', 'html', 'comp', 'js') 14 | done() 15 | } 16 | 17 | // 18 | gulp.task('watch', function(done) { 19 | gulp.start('css', 'html', 'comp', 'js') 20 | gulp.watch('./www/**/*.pug', ['comp', 'css', 'html', 'js']) 21 | done() 22 | }) 23 | // 24 | 25 | gulp.task('css', function () { 26 | return gulp.src('./www/assets/fr7/framework7.less') 27 | .pipe( less( 28 | { plugins: [autoprefix] } 29 | )) 30 | .pipe(gulp.dest('./www/assets/fr7/')) 31 | }) 32 | 33 | gulp.task('html', function() { 34 | return gulp.src('./www/*.pug') 35 | .pipe( pug( 36 | { pretty: true } 37 | )) // pipe to pug plugin 38 | .pipe(gulp.dest('./www/')) 39 | }) 40 | 41 | gulp.task('comp', function() { 42 | return gulp.src('./www/custel/*.pug') 43 | .pipe( pug( 44 | {pretty: true } 45 | )) // pipe to pug plugin 46 | .pipe( rename({ extname: '.tag'})) 47 | .pipe( riot( 48 | { } 49 | )) 50 | .pipe( rename({ extname: '.js'})) 51 | //.pipe(debug()) 52 | .pipe(gulp.dest('./www/custel/')) 53 | }) 54 | 55 | gulp.task('tag', function() { 56 | return gulp.src('./www/custel/*.pug') 57 | .pipe( pug( 58 | {pretty: true } 59 | )) // pipe to pug plugin 60 | .pipe( rename({ extname: '.tag'})) 61 | .pipe(gulp.dest('./www/custel/')) 62 | }) 63 | 64 | gulp.task('js', function () { 65 | return gulp.src('./www/**/*.ts') 66 | .pipe( ts( 67 | { } 68 | )) 69 | .pipe(gulp.dest('./www/')) 70 | }) -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | gitdown.yaml -------------------------------------------------------------------------------- /src/.npmignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules/ 3 | 4 | 5 | *.sh 6 | *.cmd 7 | 8 | .DS_Store 9 | 10 | .yarn 11 | 12 | -------------------------------------------------------------------------------- /src/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 8.5.2 2 | - bump 3 | 4 | ### 8.4.24 5 | - bump 6 | 7 | ### 8.4.22 8 | - moved sql 9 | 10 | ### 8.4.16 11 | - bump versions 12 | 13 | ### 8.4.14 14 | - updated logging 15 | - added acorn, but that needs to be removed after pug bumps version 16 | 17 | ### 8.3.0 18 | - versions bump 19 | 20 | ### 8.2.11 21 | - c CRUD 22 | 23 | 24 | ### 8.2.8 25 | - obfuscate all js 26 | - need proper ; or it won't work 27 | 28 | ### 8.2.3 29 | - jsonfeed in items 30 | 31 | ### 8.2.1 32 | - ts emits es ES2018 (level 9) 33 | - ( but js in html work to level 8 (ES2017), should avoid .js in html anyway ) 34 | 35 | ### 7.14.15 36 | - moved download ops to agentg 37 | 38 | ### 7.14.7 39 | - new year 40 | 41 | ### 7.12.4 42 | - new RPC signatures, also include w/ lz 43 | 44 | ### 6.11.12 45 | - bunyan format 2 46 | - moved DB to mbakex 47 | 48 | ### 6.11.4 49 | - added -p to get port + pid 50 | 51 | ### 6.11.2 52 | - tsc bump 53 | - minor server trace 54 | 55 | ### 6.10.20 56 | - remove CSV. use client side yaml 57 | 58 | ### 6.10.19 59 | - changed to csv from csv2json 60 | - remove tracer for winston 61 | - convert logger to bunyon 62 | 63 | ### 6.10.15 64 | - bump -sass for pre node v13 65 | 66 | ### 6.10.14 67 | - update tsc config 68 | 69 | ### 6.10.11 70 | - remove riot 71 | 72 | ### 6.10.10 73 | - set x time headers in serv 74 | 75 | ### 6.10.9 76 | - add SysAgent and Invoke 77 | 78 | ### 6.10.8 79 | - switched to better 3 for SQLite lib 80 | 81 | ### 6.10.5 82 | - remove '2' methods for RPC, and use proper name 83 | 84 | ### 6.10.2 85 | - rename assets.yaml to style.yaml 86 | 87 | ### 6.10.1 88 | - rename assets.yaml to style.yaml 89 | 90 | ### 6.10.0 91 | - remove old invoke methods for RPC !!! 92 | - added systeminformation and mysql 93 | - renamed BaseDB to L for sqllite 94 | 95 | ### 06.09.30 96 | - removed frags from -i items.json 97 | 98 | ### 06.09.25 99 | - added UI Binding in mbake -f 100 | 101 | ### 06.09.19 102 | - RPC pre update -2 103 | 104 | ### 06.09.18 105 | - RPC pre update 106 | 107 | ### 06.09.17 108 | - markdown image = 90% support 109 | 110 | ### 06.09.16 111 | - Static to use CDN/Browser 112 | - Added Pug Intro example 113 | 114 | ### 06.09.12 115 | - added interface for DB 116 | 117 | ### 6.08.28 118 | - touched up srv file 119 | - bump versions 120 | 121 | ### 6.08.21 122 | - touched up base DB class 123 | 124 | ### 6.08.13 125 | - changed `getFolders()` method to get all directories, instead of showing only folders containing `.pug` and `dat.yaml` 126 | 127 | ### 6.08.12 128 | - wcomp must now be -custel 129 | 130 | ### 6.08.9 131 | - version bump 132 | 133 | ### 6.08.2 134 | - Changed repos 135 | 136 | ### 6.07.32 137 | - Srv to list dirs 138 | 139 | ### 6.07.29 140 | - added email 141 | - -f has baseVM 142 | 143 | ### 6.07.25 144 | - comments for GLO are less 145 | 146 | ### 6.07.24 147 | - retErr(resp, msg) in Srv to allow returning error 148 | - also iCheck requires retErr implementation 149 | -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | All rights reserved by Cekvenich(https://www.INTUITION.DEV) | Cekvenich 2 | 3 | License: LGPL-3.0-only 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/PugServ.js: -------------------------------------------------------------------------------- 1 | let source = ` 2 | header 3 | body 4 | p Hello #{key1} 5 | `; 6 | let options = { key1: 'World' }; 7 | let html = pug.renderFile(source, options); 8 | export {}; 9 | -------------------------------------------------------------------------------- /src/PugServ.ts: -------------------------------------------------------------------------------- 1 | 2 | import pug = require('pug') 3 | 4 | let source =` 5 | header 6 | body 7 | p Hello #{key1} 8 | ` 9 | let options = {key1: 'World'} 10 | 11 | let html = pug.renderFile(source, options) 12 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | 2 | # MetaBake ( Intuition v1 ) 3 | 4 | ## 'All my friends KNOW the low-coder' 5 | 6 | #### mbake CLI is open source and extensible low-code productivity bundler/builder that leverages Pug and JAMstack. 7 | 8 | ## Overview 9 | 10 | MetaBake(mbake) provides a bundler/builder for a developer to write cleaner code quicker, with `pug` and livereload out of the box. 11 | 12 | You can gradually adopt it while it allows you to develop quicker - and with clean simplicity - Static Websites, Custom Elements, custom CMS/E-Commerce, CRUD and all sorts of dynamic Web Apps. 13 | 14 | #### Please star our main project here: 15 | - https://github.com/INTUITION-dev/INTU 16 | 17 | ### Prerequisites 18 | 19 | You should know HTML, CSS and JavaScript - that is all we use. If you need to catch up, we recommend this book: 'Design and Build Websites' by Jon Duckett. 20 | 21 | 22 | ## Quick start 23 | 24 | 25 | ```sh 26 | npm i -g mbake 27 | mbake -w . /* for a base website */ 28 | cd website 29 | mbake -w . /* to run the watcher livereload */ 30 | ``` 31 | 32 | ## INTUITION in 4 Minutes 33 | 34 | Building sites take a few moments, just add `index.pug` and `dat.yaml` files in the folder, and compile it with `mbake .` from the root folder of your site. 35 | 36 | 37 | ### Example 38 | Create a folder called 'one'. 39 | In the folder 'one', create file index.pug 40 | 41 | ```pug 42 | 43 | header 44 | body 45 | p Hello #{key1} 46 | 47 | ``` 48 | 49 | and create file dat.yaml 50 | ```yaml 51 | key1: World 52 | ``` 53 | > Note: to create a new page|screen in mbake, create a new folder with an index.pug and day.yaml in each folder. 54 | 55 | 56 | ### Now make with mbake: 57 | 58 | ```sh 59 | mbake . 60 | ``` 61 | 62 | This will create `index.html`. Of course you can use regular Pug syntax to include other Pug files or Markdown. (INTUITION Markdown flavor includes CSS support): 63 | ```pug 64 | body 65 | div 66 | include:metaMD comment.md 67 | ``` 68 | 69 | And example Markdown file with CSS nested classes. Title is nested in 2 levels, .column class CSS and second level .stick CSS class 70 | ``` 71 | :::: column col-2 72 | ::: stick 73 | Title 74 | ::: 75 | :::: 76 | 77 | ``` 78 | 79 | 80 | So if you write a Markdown file comment.md, it will be included in index.html 81 | --- 82 | 83 | 84 | ### Watcher 85 | 86 | This will start a webserver and auto-refresh browser, and watch for file changes to auto build: 87 | ```sh 88 | mbakeX -w . 89 | ``` 90 | 91 | Instead of `.` you can specify any path. 92 | Also, the fact that we are generating this static content allows us to have the entire Web App served by a CDN 93 | 94 | 95 | ## SASS 96 | CSS can be hard to work with, so people use Sass/Scss. Create a `style.scss` file: 97 | ```css 98 | $font-stack: Helvetica, sans-serif; 99 | $primary-color: #333; 100 | 101 | body { 102 | font: 100% $font-stack; 103 | color: $primary-color; 104 | } 105 | ``` 106 | Create file `style.yaml` in assets folder, to compile your scss to css 107 | 108 | ``` 109 | css: 110 | - style.scss 111 | ``` 112 | 113 | and run 114 | ```sh 115 | mbake -s . 116 | ``` 117 | 118 | It will create a css file in `assets/css` with auto-prefixes. 119 | 120 | So the structure of asset folder should look something like that: 121 | ```folder 122 | assets/ 123 | css/style.css /* this is going to be compiled from style.scss */ 124 | scss/style.scss /* your working area */ 125 | style.yaml /* with `scss` files that need to be compiled */ 126 | ... 127 | ``` 128 | 129 | ## TypeScript 130 | 131 | TypeScript is supper-set of JavaScript. Write a ts file, like foo.ts: 132 | ```ts 133 | foo(i:number) { 134 | console.log('oh hi') 135 | } 136 | ``` 137 | 138 | and run 139 | ```sh 140 | mbake -t . 141 | ``` 142 | 143 | 144 | Lots of time you use .ts to call DB services: such as Google FireStore. 145 | 146 | ## Examples - Website 147 | 148 | There are 12 very different examples included in the mbake CLI. One is just a website: 149 | ```sh 150 | mbake -w 151 | ``` 152 | 153 | That will extract an example website in the current folder. ( Obviously you can create any layout with any combination of css and other libraries, but here is how we laid out an example/starter website). 154 | 155 | 156 | 157 | ```sh 158 | mbake -f . 159 | ``` 160 | 161 | This emits a Pug file that you should include in your Pug's layout head section. 162 | In turn, the included file calls a mbToolBelt.js from a CDN. 163 | 164 | 165 | ## Apps 166 | 167 | While you can build websites: you can also build full webapps, eg. CRUD. 168 | 169 | 170 | **Other examples include:** 171 | 172 | - Using markdown CSS effect: allows non-programmers to write interactive stories 173 | - Slide show with markdown 174 | - Dashboard example 175 | - Ads example 176 | 177 | 178 | 179 | # Links 180 | 181 | [Advanced Front End w/ Pug](https://github.com/intuition-dev/mbCLI/tree/master/pug) 182 | 183 | [mbake CLI Docs](http://intuition-dev.github.io/mbCLI) 184 | 185 | [Git Repo](http://git.metabake.net) 186 | 187 | [Pug example](http://pug.metabake.net) 188 | 189 | [INTUITION.DEV Home Page](https://www.INTUITION.DEV) -------------------------------------------------------------------------------- /src/lib/Base.js: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | // NOTE: You can extend any classes! 3 | export class Ver { 4 | static ver() { 5 | return 'v8.5.2'; 6 | } 7 | static date() { 8 | return new Date().toISOString(); 9 | } 10 | } 11 | import { TerseB } from "terse-b/terse-b"; 12 | const log = new TerseB('base'); 13 | const path = require('path'); 14 | import { MinJS } from './Extra'; 15 | import { Dat } from './FileOpsBase'; 16 | const { Dirs } = require('agentg/lib/FileOpsExtra'); 17 | const minify = require('html-minifier').minify; 18 | const Terser = require("terser"); 19 | const mad = require('markdown-it')({ 20 | html: true, 21 | typographer: true, 22 | linkify: true 23 | }).use(require('markdown-it-imsize')); // eg![](main.jpg =50%x50%) 24 | mad.use(markdownItCont, 'dynamic', { 25 | // https://github.com/markdown-it/markdown-it-container/issues/23 26 | validate: function () { return true; }, 27 | render: function (tokens, idx) { 28 | let token = tokens[idx]; 29 | if (token.nesting === 1) { 30 | return '\n
'; 31 | } 32 | else { 33 | return '
\n'; 34 | } 35 | } 36 | }); 37 | export class MBake { 38 | bake(path_, prod) { 39 | return new Promise(function (resolve, reject) { 40 | if (!path_ || path_.length < 1) { 41 | log.info('no path_ arg passed'); 42 | reject('no path_ arg passed'); 43 | } 44 | try { 45 | log.info(' Baking ' + path_); 46 | let d = new Dirs(path_); 47 | let dirs = d.getFolders(); 48 | if (!dirs || dirs.length < 1) { 49 | //go one up 50 | path_ = Dirs.goUpOne(path_); 51 | log.info(' New Dir: ', path_); 52 | d = new Dirs(path_); 53 | dirs = d.getFolders(); 54 | } 55 | for (let val of dirs) { 56 | let n = new BakeWrk(val); 57 | n.bake(prod); 58 | } 59 | resolve('OK'); 60 | } 61 | catch (err) { 62 | log.warn(err); 63 | reject(err); 64 | } 65 | }); //pro 66 | } //() 67 | // itemize and bake 68 | itemizeNBake(ppath_, prod) { 69 | let _this = this; 70 | return new Promise(function (resolve, reject) { 71 | if (!ppath_ || ppath_.length < 1) { 72 | log.warn('no path_ arg passed'); 73 | reject('no path arg passed'); 74 | } 75 | log.info('ib:', ppath_); 76 | try { 77 | const i = new JsonFeed(ppath_); 78 | i.itemize(); 79 | } 80 | catch (err) { 81 | log.warn(err); 82 | reject(err); 83 | } 84 | _this.bake(ppath_, prod) 85 | .then(function () { resolve('OK'); }) 86 | .catch(function (err) { 87 | log.warn(err); 88 | reject(err); 89 | }); 90 | }); //pro 91 | } //() 92 | } //() 93 | export class BakeWrk { 94 | constructor(dir_) { 95 | let dir = Dirs.slash(dir_); 96 | this.dir = dir; 97 | console.info(' processing: ' + this.dir); 98 | } 99 | static metaMD(text, options) { 100 | log.info(' ', options); 101 | return mad.render(text); 102 | } 103 | /* 104 | static marp(text, options) {//a custom md filter that uses a transformer 105 | log.info(' ', options) 106 | const { html, css } = marpit.render(text) 107 | return html 108 | } 109 | */ 110 | static minify_pg(text, inline) { 111 | let code = text.match(/^\s*\s*$/) ? '' : text; 112 | let result = Terser.minify(code, MinJS.CompOptionsES); 113 | if (result.error) { 114 | log.warn('Terser error:', result.error); 115 | return text; 116 | } 117 | return result.code.replace(/;$/, ''); 118 | } 119 | //find string indexes 120 | static sindexes(source, f) { 121 | if (!source) 122 | return []; 123 | if (!f) 124 | return []; 125 | let result = []; 126 | for (let i = 0; i < source.length; ++i) { 127 | if (source.substring(i, i + f.length) == f) 128 | result.push(i); 129 | } 130 | return result; 131 | } 132 | bake(prod) { 133 | let tstFile = this.dir + '/index.pug'; 134 | if (!fs.existsSync(tstFile)) { 135 | return; 136 | } 137 | process.chdir(this.dir); 138 | console.info(this.dir); 139 | let dat = new Dat(this.dir); 140 | //static data binding with a custom md filter that uses a transformer 141 | let options = dat.getAll(); 142 | options['filters'] = { 143 | metaMD: BakeWrk.metaMD, 144 | }; 145 | options['ENV'] = prod; 146 | //*GLOBAL yaml 147 | const global = options['GLO']; 148 | if (global) { 149 | const ps = this.dir + '/' + global; 150 | const p = path.normalize(ps + '/GLO.yaml'); 151 | let glo = yaml.load(fs.readFileSync(p)); 152 | options = Object.assign(glo, options); 153 | //log.info(options) 154 | } //() 155 | if (this.locAll(options)) // if locale, we are not writing here, but in sub folders. 156 | return ' '; 157 | this.writeFilePg(this.dir + '/index.pug', options, this.dir + '/index.html'); 158 | //amp 159 | if (!fs.existsSync(this.dir + '/m.pug')) 160 | return ' '; 161 | this.writeFilePg(this.dir + '/m.pug', options, this.dir + '/m.html'); 162 | } //() 163 | // if loc, do locale 164 | locAll(options) { 165 | if (!options.LOC) 166 | return false; 167 | let d = options.LOC; 168 | d = this.dir + d; 169 | let a; 170 | let fn = d + '/loc.yaml'; 171 | if (fs.existsSync(fn)) 172 | a = yaml.load(fs.readFileSync(fn)); 173 | else { 174 | let dir2 = findUp.sync('loc.yaml', { cwd: d }); 175 | a = yaml.load(fs.readFileSync(dir2)); 176 | d = dir2.slice(0, -8); 177 | } 178 | // found 179 | const css = a.loc; 180 | const set = new Set(css); 181 | log.info(set); 182 | let merged = { ...a, ...options }; // es18 spread 183 | for (let item of set) { 184 | this.do1Locale(item, merged); 185 | } 186 | //delete 'root' index.html 187 | fs.remove(this.dir + '/index.html'); 188 | } //() 189 | do1Locale(locale, combOptions) { 190 | //extract locale var 191 | console.info(locale); 192 | let localeProps = {}; 193 | localeProps['LOCALE'] = locale; // any let can be access in pug or js eg window.locale = '#{LOCALE}' 194 | for (let key in combOptions) 195 | if (key.endsWith('-' + locale)) { //for each key 196 | let len = key.length - ('-' + locale).length; 197 | let key2 = key.substring(0, len); 198 | localeProps[key2] = combOptions[key]; 199 | } 200 | let locMerged = { ...combOptions, ...localeProps }; // es18 spread 201 | log.info(localeProps); 202 | // if dir not exists 203 | let locDir = this.dir + '/' + locale; 204 | log.info(locDir); 205 | fs.ensureDirSync(locDir); 206 | // if loc.pug exists 207 | if (fs.existsSync(locDir + '/loc.pug')) 208 | this.writeFilePg(locDir + '/loc.pug', locMerged, locDir + '/index.html'); 209 | else 210 | this.writeFilePg(this.dir + '/index.pug', locMerged, locDir + '/index.html'); 211 | //amp 212 | if (!fs.existsSync(this.dir + '/m.pug')) 213 | return ' '; 214 | this.writeFilePg(this.dir + '/m.pug', locMerged, locDir + '/m.html'); 215 | } 216 | writeFilePg(source, options, target) { 217 | let html = pug.renderFile(source, options); 218 | const ver = ''; 219 | if (!options['pretty']) 220 | html = minify(html, BakeWrk.minifyPg); 221 | html = html.replace(BakeWrk.ebodyHtml, ver + BakeWrk.ebodyHtml); 222 | fs.writeFileSync(target, html); 223 | } 224 | } //class 225 | BakeWrk.ebodyHtml = ''; 226 | BakeWrk.minifyPg = { 227 | caseSensitive: true, 228 | collapseWhitespace: true, 229 | decodeEntities: true, 230 | minifyCSS: true, 231 | minifyJS: BakeWrk.minify_pg, 232 | quoteCharacter: "'", 233 | removeComments: true, 234 | removeScriptTypeAttributes: true, 235 | removeStyleLinkTypeAttributes: true, 236 | useShortDoctype: true, 237 | sortAttributes: true, 238 | sortClassName: true 239 | }; 240 | export class JsonFeed { 241 | constructor(dir_) { 242 | let dir = Dirs.slash(dir_); 243 | let fn = dir + '/dat_i.yaml'; 244 | if (!fs.existsSync(fn)) { //if it does not exist, go up a level 245 | let dir2 = findUp.sync('dat_i.yaml', { cwd: dir }); 246 | if (dir2 != null) { 247 | dir = dir2.slice(0, -11); // this reported error for UBAYCAP 248 | } 249 | } 250 | this.dir = dir; 251 | let d = new Dirs(dir); 252 | this.dirs = d.getFolders(); 253 | } 254 | _addAnItem(dn) { 255 | try { 256 | if (!fs.existsSync(dn + '/dat.yaml')) 257 | return; 258 | let y = yaml.load(fs.readFileSync(dn + '/dat.yaml')); 259 | if (!y) 260 | return; 261 | //if publish property: true, skip static publishing regardless of publishDate 262 | if (false == y.publish) { 263 | return; 264 | } 265 | // if publishDate is set and later than now we skip 266 | if (typeof y.publishDate !== 'undefined' 267 | && y.publishDate !== null 268 | && (y.publishDate - Date.now()) > 0) { 269 | return; 270 | } 271 | JsonFeed.clean(y); 272 | let dl = dn.lastIndexOf('/'); 273 | let url = dn.substring(dl + 1); 274 | log.info('', url); 275 | y.url = url; 276 | if (!y.hasOwnProperty('id')) 277 | y.id = url; //to be compliant to feed 278 | //array of items 279 | if (!this.feed.items) 280 | this.feed.items = []; 281 | y.index = this.feed.items.length; 282 | //log.info('', this.feed.items.length) 283 | this.feed.items.push(y); 284 | } 285 | catch (err) { 286 | log.warn(err); 287 | } 288 | } 289 | itemize() { 290 | console.info('Itemizing: ' + this.dir); 291 | const rootDir = this.dir; 292 | // header file 293 | let fn = rootDir + '/dat_i.yaml'; 294 | if (!fs.existsSync(fn)) 295 | return; 296 | let y = yaml.load(fs.readFileSync((fn))); 297 | JsonFeed.clean(y); 298 | y.mbVer = Ver.ver(); 299 | this.feed = y; 300 | log.warn(this.feed); 301 | for (let val of this.dirs) { 302 | this._addAnItem(val); 303 | } 304 | if (!this.feed.items) 305 | this.feed.items = []; 306 | if (0 == this.feed.items.length) { 307 | log.info('no items'); 308 | return; 309 | } 310 | this.feed.count = this.feed.items.length; 311 | //write 312 | let json = JSON.stringify(this.feed, null, 2); 313 | let items = rootDir + '/jsonfeed.json'; 314 | fs.writeFileSync(items, json); 315 | log.info(' processed.'); 316 | return ' processed '; 317 | } 318 | static clean(o) { 319 | delete o['basedir']; 320 | delete o['ROOT']; 321 | delete o['pretty']; 322 | delete o['LOC']; 323 | delete o['frags']; 324 | } 325 | } //class 326 | -------------------------------------------------------------------------------- /src/lib/Base.ts: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | // NOTE: You can extend any classes! 3 | 4 | export class Ver { 5 | static ver() { 6 | return 'v8.5.2' 7 | } 8 | static date(): string { 9 | return new Date().toISOString() 10 | } 11 | } 12 | 13 | import { TerseB } from "terse-b/terse-b" 14 | 15 | const log:any = new TerseB('base') 16 | 17 | const path = require('path') 18 | import { MinJS } from './Extra' 19 | import { Dat } from './FileOpsBase' 20 | 21 | const {Dirs} = require('agentg/lib/FileOpsExtra') 22 | 23 | import fs = require('fs-extra') 24 | import yaml = require('js-yaml') 25 | 26 | import findUp = require('find-up') 27 | 28 | import pug = require('pug') 29 | const minify = require('html-minifier').minify 30 | const Terser = require("terser") 31 | 32 | // code ///////////////////////////////////////////////////////////////////// 33 | 34 | // metaMD Mad 35 | import markdownItCont = require('markdown-it-container') 36 | 37 | const mad = require('markdown-it')({ 38 | html: true, 39 | typographer: true, 40 | linkify: true 41 | }).use(require('markdown-it-imsize')) // eg![](main.jpg =50%x50%) 42 | mad.use(markdownItCont, 'dynamic', { 43 | // https://github.com/markdown-it/markdown-it-container/issues/23 44 | validate: function () { return true; }, 45 | render: function (tokens, idx) { 46 | let token = tokens[idx] 47 | 48 | if (token.nesting === 1) { 49 | return '\n
' 50 | } else { 51 | return '
\n' 52 | } 53 | } 54 | }) 55 | 56 | export class MBake { 57 | 58 | bake(path_, prod: number): Promise { 59 | return new Promise(function (resolve, reject) { 60 | if (!path_ || path_.length < 1) { 61 | log.info('no path_ arg passed') 62 | reject('no path_ arg passed') 63 | } 64 | try { 65 | log.info(' Baking ' + path_) 66 | 67 | let d = new Dirs(path_) 68 | let dirs = d.getFolders() 69 | 70 | if (!dirs || dirs.length < 1) { 71 | //go one up 72 | path_ = Dirs.goUpOne(path_) 73 | log.info(' New Dir: ', path_) 74 | d = new Dirs(path_) 75 | dirs = d.getFolders() 76 | } 77 | 78 | for (let val of dirs) { 79 | let n = new BakeWrk(val) 80 | n.bake(prod) 81 | } 82 | resolve('OK') 83 | } catch (err) { 84 | log.warn(err) 85 | reject(err) 86 | } 87 | })//pro 88 | }//() 89 | 90 | // itemize and bake 91 | itemizeNBake(ppath_, prod: number): Promise { 92 | let _this = this 93 | return new Promise(function (resolve, reject) { 94 | if (!ppath_ || ppath_.length < 1) { 95 | log.warn('no path_ arg passed') 96 | reject('no path arg passed') 97 | } 98 | log.info('ib:', ppath_) 99 | 100 | try { 101 | const i = new JsonFeed(ppath_) 102 | i.itemize() 103 | 104 | } catch (err) { 105 | log.warn(err) 106 | reject(err) 107 | } 108 | 109 | _this.bake(ppath_, prod) 110 | .then(function () { resolve('OK') }) 111 | .catch(function (err) { 112 | log.warn(err) 113 | reject(err) 114 | }) 115 | 116 | })//pro 117 | }//() 118 | 119 | }//() 120 | 121 | export class BakeWrk { 122 | dir: string 123 | static ebodyHtml = '' 124 | 125 | constructor(dir_: string) { 126 | let dir = Dirs.slash(dir_) 127 | 128 | this.dir = dir 129 | console.info(' processing: ' + this.dir) 130 | } 131 | 132 | static metaMD(text, options) {//a custom md filter that uses a transformer 133 | log.info(' ', options) 134 | return mad.render(text) 135 | } 136 | 137 | /* 138 | static marp(text, options) {//a custom md filter that uses a transformer 139 | log.info(' ', options) 140 | const { html, css } = marpit.render(text) 141 | return html 142 | } 143 | */ 144 | 145 | static minify_pg(text, inline) { 146 | 147 | let code = text.match(/^\s*\s*$/) ? '' : text 148 | 149 | let result = Terser.minify(code, MinJS.CompOptionsES) 150 | 151 | if (result.error) { 152 | log.warn('Terser error:', result.error) 153 | return text 154 | } 155 | return result.code.replace(/;$/, '') 156 | } 157 | 158 | //find string indexes 159 | static sindexes(source, f) { 160 | if (!source) 161 | return [] 162 | if (!f) 163 | return [] 164 | 165 | let result = [] 166 | for (let i = 0; i < source.length; ++i) { 167 | if (source.substring(i, i + f.length) == f) 168 | result.push(i) 169 | } 170 | return result 171 | } 172 | 173 | static minifyPg = { 174 | caseSensitive: true, 175 | collapseWhitespace: true, 176 | decodeEntities: true, 177 | minifyCSS: true, 178 | minifyJS: BakeWrk.minify_pg, 179 | quoteCharacter: "'", 180 | removeComments: true, 181 | removeScriptTypeAttributes: true, 182 | removeStyleLinkTypeAttributes: true, 183 | useShortDoctype: true, 184 | sortAttributes: true, 185 | sortClassName: true 186 | } 187 | 188 | bake(prod: number) { 189 | let tstFile = this.dir + '/index.pug' 190 | if (!fs.existsSync(tstFile)) { 191 | return 192 | } 193 | process.chdir(this.dir) 194 | console.info(this.dir) 195 | 196 | let dat = new Dat(this.dir) 197 | 198 | //static data binding with a custom md filter that uses a transformer 199 | let options = dat.getAll() 200 | options['filters'] = { 201 | metaMD: BakeWrk.metaMD, 202 | //marp: BakeWrk.marp 203 | } 204 | 205 | options['ENV'] = prod 206 | 207 | //*GLOBAL yaml 208 | const global = options['GLO'] 209 | if (global) { 210 | const ps = this.dir + '/' + global 211 | const p = path.normalize(ps + '/GLO.yaml') 212 | let glo = yaml.load(fs.readFileSync(p)) 213 | 214 | options = Object.assign(glo, options) 215 | //log.info(options) 216 | }//() 217 | 218 | if (this.locAll(options)) // if locale, we are not writing here, but in sub folders. 219 | return ' ' 220 | 221 | this.writeFilePg(this.dir + '/index.pug', options, this.dir + '/index.html') 222 | //amp 223 | if (!fs.existsSync(this.dir + '/m.pug')) 224 | return ' ' 225 | this.writeFilePg(this.dir + '/m.pug', options, this.dir + '/m.html') 226 | 227 | }//() 228 | 229 | // if loc, do locale 230 | locAll(options) { 231 | if (!options.LOC) return false 232 | 233 | let d = options.LOC 234 | d = this.dir + d 235 | 236 | let a 237 | let fn = d + '/loc.yaml' 238 | if (fs.existsSync(fn)) 239 | a = yaml.load(fs.readFileSync(fn)) 240 | else { 241 | let dir2: string = findUp.sync('loc.yaml', { cwd: d }) 242 | a = yaml.load(fs.readFileSync(dir2)) 243 | d = dir2.slice(0, -8) 244 | } 245 | // found 246 | const css: string[] = a.loc 247 | const set: Set = new Set(css) 248 | log.info(set) 249 | 250 | let merged = { ...a, ...options } // es18 spread 251 | for (let item of set) { 252 | this.do1Locale(item, merged) 253 | } 254 | 255 | //delete 'root' index.html 256 | fs.remove(this.dir + '/index.html') 257 | }//() 258 | 259 | do1Locale(locale, combOptions) { 260 | //extract locale var 261 | console.info(locale) 262 | let localeProps = {} 263 | localeProps['LOCALE'] = locale // any let can be access in pug or js eg window.locale = '#{LOCALE}' 264 | 265 | for (let key in combOptions) 266 | if (key.endsWith('-' + locale)) { //for each key 267 | let len = key.length - ('-' + locale).length 268 | let key2 = key.substring(0, len) 269 | localeProps[key2] = combOptions[key] 270 | } 271 | 272 | let locMerged = { ...combOptions, ...localeProps } // es18 spread 273 | log.info(localeProps) 274 | 275 | // if dir not exists 276 | let locDir = this.dir + '/' + locale 277 | log.info(locDir) 278 | fs.ensureDirSync(locDir) 279 | 280 | // if loc.pug exists 281 | if (fs.existsSync(locDir + '/loc.pug')) 282 | this.writeFilePg(locDir + '/loc.pug', locMerged, locDir + '/index.html') 283 | else this.writeFilePg(this.dir + '/index.pug', locMerged, locDir + '/index.html') 284 | 285 | //amp 286 | if (!fs.existsSync(this.dir + '/m.pug')) 287 | return ' ' 288 | this.writeFilePg(this.dir + '/m.pug', locMerged, locDir + '/m.html') 289 | } 290 | 291 | writeFilePg(source, options, target) { 292 | let html = pug.renderFile(source, options) 293 | const ver = '' 294 | if (!options['pretty']) 295 | html = minify(html, BakeWrk.minifyPg) 296 | html = html.replace(BakeWrk.ebodyHtml, ver + BakeWrk.ebodyHtml) 297 | fs.writeFileSync(target, html) 298 | } 299 | 300 | }//class 301 | 302 | 303 | export class JsonFeed { 304 | dir: string 305 | dirs // array 306 | feed //rss 307 | 308 | constructor(dir_: string) { 309 | let dir = Dirs.slash(dir_) 310 | let fn: string = dir + '/dat_i.yaml' 311 | 312 | if (!fs.existsSync(fn)) { //if it does not exist, go up a level 313 | let dir2: string = findUp.sync('dat_i.yaml', { cwd: dir }) 314 | 315 | if (dir2 != null) { 316 | dir = dir2.slice(0, -11) // this reported error for UBAYCAP 317 | } 318 | } 319 | 320 | this.dir = dir 321 | let d = new Dirs(dir) 322 | this.dirs = d.getFolders() 323 | } 324 | 325 | _addAnItem(dn) { 326 | try { 327 | if (!fs.existsSync(dn + '/dat.yaml')) 328 | return 329 | 330 | let y = yaml.load(fs.readFileSync(dn + '/dat.yaml')) 331 | if (!y) return 332 | 333 | //if publish property: true, skip static publishing regardless of publishDate 334 | if (false == y.publish) { 335 | return 336 | } 337 | 338 | // if publishDate is set and later than now we skip 339 | if (typeof y.publishDate !== 'undefined' 340 | && y.publishDate !== null 341 | && (y.publishDate - Date.now()) > 0 342 | ) { 343 | return; 344 | } 345 | 346 | JsonFeed.clean(y) 347 | 348 | let dl = dn.lastIndexOf('/') 349 | let url = dn.substring(dl + 1) 350 | log.info('', url) 351 | y.url = url 352 | 353 | if (!y.hasOwnProperty('id')) 354 | y.id = url //to be compliant to feed 355 | 356 | //array of items 357 | if (!this.feed.items) 358 | this.feed.items = [] 359 | 360 | y.index = this.feed.items.length 361 | //log.info('', this.feed.items.length) 362 | this.feed.items.push(y) 363 | 364 | } catch (err) { 365 | log.warn(err) 366 | } 367 | } 368 | 369 | itemize(): string { 370 | console.info('Itemizing: ' + this.dir) 371 | 372 | const rootDir: string = this.dir 373 | // header file 374 | let fn: string = rootDir + '/dat_i.yaml' 375 | if (!fs.existsSync(fn)) return; 376 | 377 | let y = yaml.load(fs.readFileSync((fn))) 378 | 379 | JsonFeed.clean(y) 380 | y.mbVer = Ver.ver() 381 | this.feed = y 382 | log.warn(this.feed) 383 | 384 | for (let val of this.dirs) { 385 | this._addAnItem(val) 386 | } 387 | 388 | if (!this.feed.items) 389 | this.feed.items = [] 390 | 391 | if (0 == this.feed.items.length) { 392 | log.info('no items') 393 | return 394 | } 395 | this.feed.count = this.feed.items.length 396 | 397 | //write 398 | let json = JSON.stringify(this.feed, null, 2) 399 | let items = rootDir + '/jsonfeed.json' 400 | fs.writeFileSync(items, json) 401 | 402 | log.info(' processed.') 403 | return ' processed ' 404 | } 405 | 406 | static clean(o: Object) {// remove fields that are pug 407 | delete o['basedir'] 408 | delete o['ROOT'] 409 | delete o['pretty'] 410 | delete o['LOC'] 411 | delete o['frags'] 412 | 413 | } 414 | 415 | }//class 416 | -------------------------------------------------------------------------------- /src/lib/Extra.js: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | // NOTE: You can extend these classes! 3 | import { Ver } from './Base'; 4 | import { TerseB } from "terse-b/terse-b"; 5 | const log = new TerseB("extra"); 6 | import * as ts from "typescript"; 7 | const Terser = require("terser"); 8 | export class MinJS { 9 | ts(dir) { 10 | log.info(dir); 11 | const THIZ = this; 12 | return new Promise(function (resolve, reject) { 13 | const rec = FileHound.create() //recursive 14 | .paths(dir) 15 | .ext("ts") 16 | .findSync(); 17 | if (rec.length < 1) 18 | resolve('OK'); 19 | THIZ.compile(rec, { 20 | target: ts.ScriptTarget.ES2018, 21 | removeComments: true, 22 | allowJs: true, 23 | skipLibCheck: true, 24 | allowSyntheticDefaultImports: true, 25 | noImplicitThis: true, 26 | strictBindCallApply: true, 27 | lib: [ 28 | 'lib.es2018.d.ts', 'lib.es2018.promise.d.ts', 'lib.dom.iterable.d.ts', 'lib.scripthost.d.ts', 'lib.dom.d.ts', 'lib.webworker.d.ts' 29 | ] 30 | }); 31 | resolve('OK'); 32 | }); 33 | } 34 | min(dir) { 35 | const THIZ = this; 36 | return new Promise(async function (resolve, reject) { 37 | const rec = FileHound.create() //recursive 38 | .paths(dir) 39 | .ext("js") 40 | .addFilter(function (fn) { 41 | if (fn._pathname.endsWith('.min.js')) { 42 | return false; 43 | } 44 | return true; 45 | }) 46 | .findSync(); 47 | for (let fn of rec) { //clean the strings 48 | try { 49 | await THIZ._minOneJS(fn); 50 | } 51 | catch (err) { 52 | log.warn(err); 53 | reject(err); 54 | } 55 | } 56 | log.info('Done!'); 57 | resolve('OK'); 58 | }); 59 | } 60 | _minOneJS(fn) { 61 | return new Promise(async function (resolve, reject) { 62 | let result; 63 | try { 64 | log.info(fn); 65 | let code = fs.readFileSync(fn).toString('utf8'); 66 | result = Terser.minify(code, MinJS.CompOptionsES); 67 | let txt = result.code; 68 | txt = txt.replace(/(\r\n\t|\n|\r\t)/gm, '\n'); 69 | txt = txt.replace(/\n\s*\n/g, '\n'); 70 | txt = txt.trim(); 71 | //if (fn.includes('-custel')) { 72 | let ugs; 73 | try { 74 | log.info('obs', fn); 75 | ugs = JavaScriptObfuscator.obfuscate(txt, MinJS.getObOptionsXES()); 76 | txt = ugs.getObfuscatedCode(); 77 | } 78 | catch (err) { 79 | log.error('you may want to use .ts'); 80 | log.error(fn, 'error'); 81 | log.error(err); 82 | reject(err); 83 | } 84 | //} 85 | txt = MinJS.ver + txt; 86 | let fn2 = fn.slice(0, -3); 87 | fn2 = fn2 + '.min.js'; 88 | fs.writeFileSync(fn2, txt); 89 | resolve('OK'); 90 | } 91 | catch (err) { 92 | log.warn(fn, err, result); 93 | reject(err); 94 | } 95 | }); 96 | } //() 97 | static getObOptionsXES() { 98 | let t = { 99 | identifierNamesGenerator: 'hexadecimal' // for virus 100 | , 101 | disableConsoleOutput: false // setting to true breaks things 102 | , 103 | target: 'browser' //-no-eval' 104 | , 105 | stringArrayThreshold: 1, 106 | stringArrayEncoding: ['rc4'] // breaks if not 107 | , 108 | splitStrings: true, 109 | splitStringsChunkLength: 5, 110 | controlFlowFlattening: true, 111 | controlFlowFlatteningThreshold: .7 // low sec 112 | }; 113 | return t; 114 | } 115 | compile(fileNames, options_) { 116 | let program = ts.createProgram(fileNames, options_); 117 | let emitResult = program.emit(); 118 | let allDiagnostics = ts 119 | .getPreEmitDiagnostics(program) 120 | .concat(emitResult.diagnostics); 121 | allDiagnostics.forEach(diagnostic => { 122 | if (diagnostic.file) { 123 | let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); 124 | let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); 125 | log.info(`${diagnostic.file.fileName}:`, `${line + 1}:${character + 1}`, `${message}`); 126 | } 127 | else { 128 | log.info(`${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`); 129 | } 130 | }); 131 | let exitCode = emitResult.emitSkipped ? 1 : 0; 132 | log.info(`status code '${exitCode}'.`); 133 | } //() 134 | } //class 135 | MinJS.ver = '// mB ' + Ver.ver() + ' on ' + Ver.date() + '\r\n'; 136 | MinJS.CompOptionsES = { 137 | ecma: 2018, 138 | keep_classnames: true, 139 | keep_fnames: true, 140 | module: true, 141 | parse: { html5_comments: false }, 142 | compress: { 143 | drop_console: true, 144 | keep_fargs: true, reduce_funcs: false, 145 | ecma: 2018, module: true, 146 | }, 147 | mangle: false, 148 | output: { indent_level: 1, quote_style: 3, 149 | beautify: false, comments: false, ecma: 2018, 150 | inline_script: false }, 151 | }; 152 | // ////////////////////////////////////////////////////////////////// 153 | export class Sas { 154 | /** 155 | * @param dir 156 | * Find style.yaml and process each css in the style.yaml array 157 | */ 158 | css(dir) { 159 | const THIZ = this; 160 | return new Promise(async function (resolve, reject) { 161 | let a; 162 | let fn = dir + '/style.yaml'; 163 | log.info(fn); 164 | if (fs.existsSync(fn)) 165 | a = yaml.load(fs.readFileSync(fn)); 166 | else { 167 | let dir2 = findUp.sync('style.yaml', { cwd: dir }); 168 | a = yaml.load(fs.readFileSync(dir2)); 169 | dir = dir2.slice(0, -11); 170 | } 171 | log.info(dir); 172 | const css = a.css; 173 | const set = new Set(css); 174 | log.info(set); 175 | for (let item of set) { 176 | await THIZ._trans(item, dir); 177 | } 178 | log.info(); 179 | log.info(' Done!'); 180 | resolve('OK'); 181 | }); 182 | } //() 183 | _trans(fn2, dir) { 184 | let css = sass.renderSync({ 185 | file: dir + '/' + fn2, 186 | outputStyle: 'compact' 187 | }); 188 | postcss([autoprefixer]) 189 | .process(css.css, { from: undefined }).then(function (result) { 190 | log.info('autoprefixer'); 191 | result.warnings().forEach(function (warn) { 192 | log.warn(warn.toString()); 193 | }); 194 | let res = stripCssComments(result.css, { preserve: false }); 195 | // lf 196 | res = res.replace(/(\r\n\t|\n|\r\t)/gm, '\n'); 197 | res = res.replace(/\n\s*\n/g, '\n'); 198 | res = res.trim(); 199 | res = res.replace(/ /g, ' '); 200 | res = res.replace(/; /g, ';'); 201 | res = res.replace(/: /g, ':'); 202 | res = res.replace(/ }/g, '}'); 203 | res = res.replace(/ { /g, '{'); 204 | res = res.replace(/, /g, ','); 205 | //add ver string 206 | const ver = ' /* mB ' + Ver.ver() + ' on ' + Ver.date() + " */"; 207 | res = res + ver; 208 | // write the file 209 | let filename2 = path.basename(fn2); 210 | filename2 = filename2.split('.').slice(0, -1).join('.'); 211 | let filename = filename2.split('\\').pop().split('/').pop(); 212 | fs.ensureDirSync(dir + '/css'); 213 | fs.writeFileSync(dir + '/css/' + filename + '.css', res); 214 | }); 215 | } //() 216 | } //class 217 | -------------------------------------------------------------------------------- /src/lib/Extra.ts: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | // NOTE: You can extend these classes! 3 | 4 | import { Ver } from './Base' 5 | import yaml = require('js-yaml') 6 | 7 | import findUp = require('find-up') 8 | 9 | import sass = require('node-sass') 10 | import autoprefixer = require('autoprefixer') 11 | import postcss = require('postcss') 12 | import stripCssComments = require('strip-css-comments') 13 | 14 | import path = require('path') 15 | import fs = require('fs-extra') 16 | import FileHound = require('filehound') 17 | 18 | import { TerseB } from "terse-b/terse-b" 19 | 20 | const log:any = new TerseB( "extra") 21 | 22 | import JavaScriptObfuscator = require('javascript-obfuscator') 23 | import { TInputOptions } from "javascript-obfuscator/src/types/options/TInputOptions" 24 | 25 | import * as ts from "typescript" 26 | const Terser = require("terser") 27 | 28 | export class MinJS { 29 | 30 | ts(dir): Promise { 31 | log.info(dir) 32 | const THIZ = this 33 | return new Promise(function (resolve, reject) { 34 | const rec = FileHound.create() //recursive 35 | .paths(dir) 36 | .ext("ts") 37 | .findSync() 38 | if (rec.length < 1) resolve('OK') 39 | 40 | THIZ.compile(rec, { 41 | target: ts.ScriptTarget.ES2018, 42 | removeComments: true, 43 | allowJs: true, 44 | skipLibCheck: true, 45 | allowSyntheticDefaultImports: true, 46 | 47 | noImplicitThis: true, 48 | strictBindCallApply: true, 49 | 50 | lib: [ 51 | 'lib.es2018.d.ts', 'lib.es2018.promise.d.ts', 'lib.dom.iterable.d.ts', 'lib.scripthost.d.ts', 'lib.dom.d.ts', 'lib.webworker.d.ts' 52 | ] 53 | }) 54 | resolve('OK') 55 | }) 56 | } 57 | 58 | min(dir): Promise { 59 | const THIZ = this 60 | return new Promise(async function (resolve, reject) { 61 | const rec = FileHound.create() //recursive 62 | .paths(dir) 63 | .ext("js") 64 | .addFilter(function (fn) { 65 | if (fn._pathname.endsWith('.min.js')) { 66 | return false 67 | } 68 | 69 | return true 70 | }) 71 | .findSync() 72 | for (let fn of rec) {//clean the strings 73 | try { 74 | await THIZ._minOneJS(fn) 75 | } catch (err) { 76 | log.warn(err) 77 | reject(err) 78 | } 79 | } 80 | log.info('Done!') 81 | resolve('OK') 82 | }) 83 | } 84 | 85 | _minOneJS(fn) { 86 | return new Promise(async function (resolve, reject) { 87 | let result 88 | try { 89 | log.info(fn) 90 | let code: string = fs.readFileSync(fn).toString('utf8') 91 | result = Terser.minify(code, MinJS.CompOptionsES) 92 | 93 | let txt = result.code 94 | 95 | txt = txt.replace(/(\r\n\t|\n|\r\t)/gm, '\n') 96 | txt = txt.replace(/\n\s*\n/g, '\n') 97 | txt = txt.trim() 98 | 99 | //if (fn.includes('-custel')) { 100 | let ugs 101 | try { 102 | log.info('obs', fn) 103 | 104 | ugs = JavaScriptObfuscator.obfuscate(txt, MinJS.getObOptionsXES()) 105 | txt = ugs.getObfuscatedCode() 106 | 107 | } catch (err) { 108 | log.error('you may want to use .ts') 109 | log.error(fn, 'error') 110 | log.error(err) 111 | reject(err) 112 | } 113 | //} 114 | 115 | txt = MinJS.ver + txt 116 | 117 | let fn2 = fn.slice(0, -3) 118 | fn2 = fn2 + '.min.js' 119 | fs.writeFileSync(fn2, txt) 120 | resolve('OK') 121 | } catch (err) { 122 | log.warn(fn, err, result) 123 | reject(err) 124 | } 125 | }) 126 | }//() 127 | 128 | 129 | static ver = '// mB ' + Ver.ver() + ' on ' + Ver.date() + '\r\n' 130 | 131 | static CompOptionsES = { // terser 132 | ecma: 2018, 133 | keep_classnames: true, 134 | keep_fnames: true, 135 | module: true, 136 | 137 | parse: { html5_comments: false }, 138 | compress: { 139 | drop_console: true, 140 | keep_fargs: true, reduce_funcs: false, 141 | ecma: 2018, module: true, 142 | }, 143 | mangle: false, // this breaks things in pg 144 | output: { indent_level: 1, quote_style: 3, 145 | beautify: false, comments: false, ecma: 2018, 146 | inline_script: false }, 147 | 148 | } 149 | 150 | static getObOptionsXES(): TInputOptions { 151 | let t:TInputOptions = { 152 | identifierNamesGenerator: 'hexadecimal' // for virus 153 | , disableConsoleOutput: false // setting to true breaks things 154 | , target: 'browser' //-no-eval' 155 | 156 | , stringArrayThreshold: 1 157 | , stringArrayEncoding: ['rc4'] // breaks if not 158 | , splitStrings: true 159 | , splitStringsChunkLength: 5 160 | 161 | , controlFlowFlattening: true 162 | , controlFlowFlatteningThreshold: .7 // low sec 163 | 164 | } 165 | return t as TInputOptions 166 | } 167 | 168 | compile(fileNames: string[], options_: ts.CompilerOptions): void { // http://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API 169 | let program = ts.createProgram(fileNames, options_) 170 | let emitResult = program.emit() 171 | 172 | let allDiagnostics = ts 173 | .getPreEmitDiagnostics(program) 174 | .concat(emitResult.diagnostics) 175 | 176 | allDiagnostics.forEach(diagnostic => { 177 | if (diagnostic.file) { 178 | let { line, character } = diagnostic.file.getLineAndCharacterOfPosition( 179 | diagnostic.start! 180 | ); 181 | let message = ts.flattenDiagnosticMessageText( 182 | diagnostic.messageText, 183 | "\n" 184 | ); 185 | log.info(`${diagnostic.file.fileName}:`, `${line + 1}:${character + 1}`, `${message}`); 186 | } else { 187 | log.info( 188 | `${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}` 189 | ); 190 | } 191 | }); 192 | 193 | let exitCode = emitResult.emitSkipped ? 1 : 0; 194 | log.info(`status code '${exitCode}'.`); 195 | }//() 196 | 197 | }//class 198 | 199 | // ////////////////////////////////////////////////////////////////// 200 | 201 | export class Sas { 202 | 203 | /** 204 | * @param dir 205 | * Find style.yaml and process each css in the style.yaml array 206 | */ 207 | css(dir): Promise { 208 | const THIZ = this 209 | return new Promise(async function (resolve, reject) { 210 | let a 211 | let fn = dir + '/style.yaml' 212 | log.info(fn) 213 | if (fs.existsSync(fn)) 214 | a = yaml.load(fs.readFileSync(fn)) 215 | else { 216 | let dir2: string = findUp.sync('style.yaml', { cwd: dir }) 217 | a = yaml.load(fs.readFileSync(dir2)) 218 | dir = dir2.slice(0, -11) 219 | } 220 | log.info(dir) 221 | 222 | const css: string[] = a.css 223 | const set: Set = new Set(css) 224 | log.info(set) 225 | 226 | for (let item of set) { 227 | await THIZ._trans(item, dir) 228 | } 229 | 230 | log.info() 231 | log.info(' Done!') 232 | resolve('OK') 233 | }) 234 | }//() 235 | 236 | _trans(fn2, dir) { 237 | let css = sass.renderSync({ 238 | file: dir + '/' + fn2 239 | , outputStyle: 'compact' 240 | }) 241 | 242 | postcss([autoprefixer]) 243 | .process(css.css, { from: undefined }).then(function (result) { 244 | log.info('autoprefixer') 245 | result.warnings().forEach(function (warn) { 246 | log.warn(warn.toString()) 247 | }) 248 | 249 | let res: string = stripCssComments(result.css, { preserve: false }) 250 | // lf 251 | res = res.replace(/(\r\n\t|\n|\r\t)/gm, '\n') 252 | res = res.replace(/\n\s*\n/g, '\n') 253 | res = res.trim() 254 | res = res.replace(/ /g, ' ') 255 | res = res.replace(/; /g, ';') 256 | res = res.replace(/: /g, ':') 257 | res = res.replace(/ }/g, '}') 258 | res = res.replace(/ { /g, '{') 259 | res = res.replace(/, /g, ',') 260 | 261 | //add ver string 262 | const ver = ' /* mB ' + Ver.ver() + ' on ' + Ver.date() + " */" 263 | res = res + ver 264 | 265 | // write the file 266 | let filename2 = path.basename(fn2) 267 | filename2 = filename2.split('.').slice(0, -1).join('.') 268 | let filename = filename2.split('\\').pop().split('/').pop() 269 | 270 | fs.ensureDirSync(dir + '/css') 271 | 272 | fs.writeFileSync(dir + '/css/' + filename + '.css', res) 273 | 274 | }) 275 | }//() 276 | 277 | }//class 278 | 279 | -------------------------------------------------------------------------------- /src/lib/FileOpsBase.js: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | import { TerseB } from "terse-b/terse-b"; 3 | const log = new TerseB("file ops b"); 4 | const { Dirs } = require('agentg/lib/FileOpsExtra'); 5 | export class Dat { 6 | constructor(path__) { 7 | let path_ = Dirs.slash(path__); 8 | this._path = path_; 9 | let y; 10 | if (fs.existsSync(path_ + '/dat.yaml')) 11 | y = yaml.load(fs.readFileSync(path_ + '/dat.yaml')); 12 | if (!y) 13 | y = {}; 14 | this.props = y; 15 | let keys = Object.keys(y); 16 | if (keys.includes('include')) 17 | this._addData(); 18 | } 19 | write() { 20 | return new Promise((resolve, reject) => { 21 | try { 22 | let y = yaml.dump(this.props, { 23 | skipInvalid: true, 24 | noRefs: true, 25 | noCompatMode: true, 26 | condenseFlow: true 27 | }); 28 | let p = this._path + '/dat.yaml'; 29 | fs.writeFileSync(p, y); 30 | resolve('OK'); 31 | } 32 | catch (err) { 33 | log.warn(err); 34 | reject(err); 35 | } 36 | }); //() 37 | } 38 | set(key, val) { 39 | this.props[key] = val; 40 | } 41 | _addData() { 42 | let jn = this.props.include; 43 | let fn = this._path + '/' + jn; 44 | let jso = fs.readFileSync(fn); 45 | Object.assign(this.props, JSON.parse(jso.toString())); // merge 46 | } 47 | getAll() { 48 | return this.props; 49 | } //() 50 | } //class 51 | export class FileOps { 52 | constructor(root_) { 53 | this.root = Dirs.slash(root_); 54 | } 55 | /** returns # of files with the name, used to archive ver */ 56 | count(fileAndExt) { 57 | const files = FileHound.create() 58 | .paths(this.root) 59 | .depth(0) 60 | .match(fileAndExt + '*') 61 | .findSync(); 62 | return files.length; 63 | } 64 | clone(src, dest) { 65 | return new Promise((resolve, reject) => { 66 | fs.copySync(this.root + src, this.root + dest); 67 | let p = this.root + dest; 68 | const d = new Dat(p); 69 | d.write(); 70 | log.info('copy!'); 71 | resolve('OK'); 72 | }); 73 | } //() 74 | write(destFile, txt) { 75 | log.info(this.root + destFile); 76 | fs.writeFileSync(this.root + destFile, txt); 77 | } 78 | read(file) { 79 | return fs.readFileSync(this.root + file).toString(); 80 | } 81 | remove(path) { 82 | let dir_path = this.root + path; 83 | log.info('remove:' + dir_path); 84 | if (fs.existsSync(dir_path)) { 85 | fs.readdirSync(dir_path).forEach(function (entry) { 86 | fs.unlinkSync(dir_path + '/' + entry); 87 | }); 88 | fs.rmdirSync(dir_path); 89 | } 90 | } 91 | removeFile(path) { 92 | let file_path = this.root + path; 93 | fs.unlinkSync(file_path); 94 | } 95 | } //class 96 | export class FileMethods { 97 | // get list of directories 98 | getDirs(mountPath) { 99 | let dirs = new Dirs(mountPath); 100 | let dirsToIgnore = ['.', '..']; 101 | return dirs.getShort() 102 | .map(el => el.replace(/^\/+/g, '')) //? 103 | .filter(el => !dirsToIgnore.includes(el)); 104 | } 105 | // get files in directory 106 | getFiles(mountPath, item) { 107 | let dirs = new Dirs(mountPath); 108 | let result = dirs.getInDir(item); 109 | if (item === '/') { // if root directory, remove all dirs from output, leave only files: 110 | return result.filter(file => file.indexOf('/') === -1 && !fs.lstatSync(mountPath + '/' + file).isDirectory()); 111 | } 112 | else { 113 | return result; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/lib/FileOpsBase.ts: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | 3 | 4 | import { TerseB } from "terse-b/terse-b" 5 | 6 | const log:any = new TerseB("file ops b") 7 | 8 | import fs = require('fs-extra') 9 | 10 | import yaml = require('js-yaml') 11 | import FileHound = require('filehound') 12 | 13 | const {Dirs} = require('agentg/lib/FileOpsExtra') 14 | 15 | export class Dat { 16 | props: any 17 | _path: string 18 | constructor(path__: string) { 19 | let path_ = Dirs.slash(path__) 20 | this._path = path_ 21 | 22 | let y 23 | if (fs.existsSync(path_ + '/dat.yaml')) 24 | y = yaml.load(fs.readFileSync(path_ + '/dat.yaml')) 25 | if (!y) y = {} 26 | this.props = y 27 | 28 | let keys = Object.keys(y) 29 | if (keys.includes('include')) this._addData() 30 | } 31 | 32 | write():Promise { 33 | return new Promise((resolve, reject) => { 34 | try { 35 | let y = yaml.dump(this.props, { 36 | skipInvalid: true, 37 | noRefs: true, 38 | noCompatMode: true, 39 | condenseFlow: true 40 | }) 41 | let p = this._path + '/dat.yaml' 42 | fs.writeFileSync(p, y) 43 | resolve('OK') 44 | } catch (err) { log.warn(err); reject(err) } 45 | })//() 46 | } 47 | 48 | set(key, val) { // ex: 'title', 'My First Blog' 49 | this.props[key] = val 50 | } 51 | _addData() { 52 | let jn = this.props.include 53 | let fn = this._path + '/' + jn 54 | let jso = fs.readFileSync(fn) 55 | Object.assign(this.props, JSON.parse(jso.toString())) // merge 56 | } 57 | 58 | getAll(): Object { 59 | return this.props 60 | }//() 61 | }//class 62 | 63 | export class FileOps { 64 | root 65 | constructor(root_) { 66 | this.root = Dirs.slash(root_) 67 | } 68 | 69 | /** returns # of files with the name, used to archive ver */ 70 | count(fileAndExt): number { 71 | 72 | const files = FileHound.create() 73 | .paths(this.root) 74 | .depth(0) 75 | .match(fileAndExt + '*') 76 | .findSync() 77 | 78 | return files.length 79 | } 80 | 81 | clone(src, dest): Promise { 82 | return new Promise((resolve, reject) => { 83 | 84 | fs.copySync(this.root + src, this.root + dest) 85 | 86 | let p = this.root + dest 87 | const d = new Dat(p) 88 | d.write() 89 | log.info('copy!') 90 | resolve('OK') 91 | }) 92 | }//() 93 | 94 | write(destFile, txt) { 95 | log.info(this.root + destFile) 96 | fs.writeFileSync(this.root + destFile, txt) 97 | } 98 | 99 | read(file): string { 100 | return fs.readFileSync(this.root + file).toString() 101 | } 102 | 103 | remove(path) { 104 | let dir_path = this.root + path 105 | log.info('remove:' + dir_path) 106 | if (fs.existsSync(dir_path)) { 107 | fs.readdirSync(dir_path).forEach(function (entry) { 108 | fs.unlinkSync(dir_path + '/' + entry) 109 | }) 110 | fs.rmdirSync(dir_path) 111 | } 112 | } 113 | removeFile(path) { 114 | let file_path = this.root + path 115 | fs.unlinkSync(file_path) 116 | } 117 | }//class 118 | 119 | 120 | export class FileMethods { 121 | 122 | // get list of directories 123 | getDirs(mountPath:string) { 124 | let dirs = new Dirs(mountPath); 125 | let dirsToIgnore = ['.', '..']; 126 | return dirs.getShort() 127 | .map(el => el.replace(/^\/+/g, '')) //? 128 | .filter(el => !dirsToIgnore.includes(el)); 129 | } 130 | 131 | // get files in directory 132 | getFiles(mountPath:string, item:string) { 133 | 134 | let dirs = new Dirs(mountPath); 135 | let result = dirs.getInDir(item); 136 | 137 | if (item === '/') { // if root directory, remove all dirs from output, leave only files: 138 | return result.filter(file => file.indexOf('/') === -1 && !fs.lstatSync(mountPath + '/' + file).isDirectory()); 139 | } else { 140 | return result; 141 | } 142 | } 143 | } 144 | 145 | -------------------------------------------------------------------------------- /src/lib/Wa.js: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | // NOTE: You can extend these classes! 3 | import { MBake } from './Base'; 4 | import { Sas, MinJS } from './Extra'; 5 | const { Dirs } = require('agentg/lib/FileOpsExtra'); 6 | import { TerseB } from "terse-b/terse-b"; 7 | const log = new TerseB("WA"); 8 | // watch: ///////////////////////////////////////////////////////////////////////////////////////////////// 9 | export class Wa { 10 | static watch(dir, port, reloadPort) { 11 | port = port || 8090; 12 | let ss = new MDevSrv(dir, port, reloadPort); 13 | const mp = new MetaPro(dir); 14 | let ww = new Watch(mp, dir); 15 | ww.start(250); // build x ms after saving a file 16 | console.info(' Serving on ' + 'http://localhost:' + port); 17 | log.info(' --------------------------'); 18 | log.info(''); 19 | opn('http://localhost:' + port); 20 | } //() 21 | } 22 | export class Watch { 23 | constructor(mp_, mount) { 24 | this.mp = mp_; 25 | if (mount.endsWith('/.')) { 26 | mount = mount.slice(0, -1); 27 | } 28 | this.root = mount; 29 | } 30 | start(delay_) { 31 | this.delay = delay_; 32 | log.info(' watcher starting'); 33 | log.info(this.root); 34 | let watchList = []; 35 | watchList.push(this.root + '/**/*.md'); 36 | watchList.push(this.root + '/**/*.ts'); 37 | watchList.push(this.root + '/**/*.pug'); 38 | watchList.push(this.root + '/**/*.scss'); 39 | watchList.push(this.root + '/**/*.sass'); 40 | watchList.push(this.root + '/**/*.yaml'); 41 | watchList.push(this.root + '/**/*.js'); 42 | watchList.push(this.root + '/**/*.json'); 43 | watchList.push(this.root + '/**/*.css'); 44 | log.info(watchList); 45 | this.watcher = chokidar.watch(watchList, { 46 | ignoreInitial: true, 47 | cwd: this.root, 48 | usePolling: true, 49 | useFsEvents: false, 50 | binaryInterval: delay_ * 5, 51 | interval: delay_ //time 52 | , 53 | atomic: delay_, 54 | awaitWriteFinish: { 55 | stabilityThreshold: delay_ * 1.2, 56 | pollInterval: delay_ * .5 57 | } 58 | }); 59 | let thiz = this; 60 | this.watcher.on('add', async function (path) { 61 | await thiz.autoNT(path, 'a'); 62 | }); 63 | this.watcher.on('change', async function (path) { 64 | await thiz.autoNT(path, 'c'); 65 | }); 66 | } //() 67 | refreshBro() { 68 | MDevSrv.reloadServer.reload(); 69 | } 70 | async autoNT(path_, wa) { 71 | log.info(wa); 72 | let path = Dirs.slash(path_); 73 | let p = path.lastIndexOf('/'); 74 | let folder = ''; 75 | let fn = path; 76 | if (p > 0) { 77 | folder = path.substring(0, p); 78 | fn = path.substr(p + 1); 79 | } 80 | try { 81 | log.info('WATCHED1:', folder + '/' + fn); 82 | await this.mp.autoBake(folder, fn); 83 | await this.refreshBro(); 84 | } 85 | catch (err) { 86 | log.warn(err); 87 | } 88 | } //() 89 | } //class 90 | export class MetaPro { 91 | constructor(mount) { 92 | this.b = new MBake(); 93 | this.mount = mount; 94 | log.info('MetaPro', this.mount); 95 | } 96 | bake(dir) { 97 | let folder = this.mount + '/' + dir; 98 | log.info(folder); 99 | return this.b.bake(folder, 0); 100 | } 101 | itemize(dir) { 102 | return this.b.itemizeNBake(this.mount + '/' + dir, 0); 103 | } 104 | css(dir) { 105 | return new Sas().css(this.mount + '/' + dir); 106 | } 107 | ts(dir) { 108 | const folder = this.mount + '/' + dir; 109 | const js = new MinJS(); 110 | return js.ts(folder); 111 | } 112 | // when you pass the file name, ex: watch 113 | async autoBake(folder__, file) { 114 | const folder = Dirs.slash(folder__); 115 | const ext = file.split('.').pop(); 116 | log.info('WATCHED2:', folder, ext); 117 | if (ext == 'scss' || ext == 'sass') // css 118 | return await this.css(folder); 119 | if (ext == 'ts') // ts 120 | return await this.ts(folder); 121 | if (ext == 'yaml') // bake and itemize 122 | return await this.itemize(folder); 123 | if (ext == 'md') 124 | return await this.bake(folder); 125 | if (ext == 'pug') { 126 | return await this.bake(folder); 127 | } 128 | return ('Cant process ' + ext); 129 | } //() 130 | } //class 131 | MetaPro.folderProp = 'folder'; 132 | MetaPro.srcProp = 'src'; 133 | MetaPro.destProp = 'dest'; 134 | // Meta: ////////////////////// 135 | export class MDevSrv { 136 | constructor(dir, port, reloadPort) { 137 | let app = express(); 138 | log.info(dir, port); 139 | app.set('app port', port); 140 | const rport = Number(reloadPort) || 9856; 141 | reload(app, { verbose: false, port: rport }) 142 | .then((reloadServer_) => { 143 | MDevSrv.reloadServer = reloadServer_; 144 | log.info('reloadServer'); 145 | }).catch(e => { 146 | log.info('==================e', e); 147 | }); 148 | app.set('views', dir); 149 | const bodyInterceptor = interceptor(function (req, res) { 150 | return { 151 | // Only HTML responses will be intercepted 152 | isInterceptable: function () { 153 | return /text\/html/.test(res.get('Content-Type')); 154 | }, 155 | intercept: function (body, send) { 156 | //log.info(' .') 157 | let $document = cheerio.load(body); 158 | $document('body').prepend(''); 159 | send($document.html()); 160 | } 161 | }; 162 | }); 163 | app.use(bodyInterceptor); 164 | app.use(express.static(dir)); 165 | app.listen(port, function () { 166 | log.info('dev srv ' + port); 167 | }); 168 | } //() 169 | } //class 170 | -------------------------------------------------------------------------------- /src/lib/Wa.ts: -------------------------------------------------------------------------------- 1 | // All rights reserved by Cekvenich|INTUITION.DEV) | Cekvenich, licensed under LGPL 3.0 2 | // NOTE: You can extend these classes! 3 | 4 | import { MBake} from './Base' 5 | import { Sas, MinJS } from './Extra' 6 | 7 | const {Dirs} = require('agentg/lib/FileOpsExtra') 8 | 9 | import express = require('express') 10 | 11 | import chokidar = require('chokidar') 12 | import reload = require('reload') 13 | 14 | import cheerio = require('cheerio') 15 | import interceptor = require('express-interceptor') 16 | 17 | import { TerseB } from "terse-b/terse-b" 18 | 19 | const log:any = new TerseB("WA") 20 | 21 | import opn = require('open') 22 | 23 | // watch: ///////////////////////////////////////////////////////////////////////////////////////////////// 24 | export class Wa { 25 | 26 | static watch(dir: string, port?: number, reloadPort?: number) { 27 | port = port || 8090; 28 | let ss = new MDevSrv(dir, port, reloadPort) 29 | const mp = new MetaPro(dir) 30 | let ww = new Watch(mp, dir) 31 | 32 | ww.start(250) // build x ms after saving a file 33 | 34 | console.info(' Serving on ' + 'http://localhost:' + port) 35 | log.info(' --------------------------') 36 | log.info('') 37 | opn('http://localhost:' + port) 38 | }//() 39 | } 40 | 41 | 42 | export class Watch { 43 | root 44 | watcher 45 | 46 | mp: MetaPro 47 | constructor(mp_: MetaPro, mount:string) { 48 | this.mp = mp_ 49 | if(mount.endsWith('/.')) { 50 | mount = mount.slice(0, -1) 51 | } 52 | this.root = mount 53 | } 54 | 55 | delay 56 | start(delay_) {// true for WAN 57 | this.delay = delay_ 58 | log.info(' watcher starting') 59 | log.info(this.root) 60 | let watchList = [] 61 | watchList.push(this.root+'/**/*.md') 62 | watchList.push( this.root+'/**/*.ts') 63 | watchList.push( this.root+'/**/*.pug') 64 | watchList.push(this.root+'/**/*.scss') 65 | watchList.push(this.root+'/**/*.sass') 66 | watchList.push(this.root+'/**/*.yaml') 67 | watchList.push(this.root+'/**/*.js') 68 | watchList.push(this.root+'/**/*.json') 69 | watchList.push(this.root+'/**/*.css') 70 | 71 | log.info(watchList) 72 | this.watcher = chokidar.watch(watchList, { 73 | ignoreInitial: true, 74 | cwd: this.root, 75 | usePolling: true, // for linux support 76 | useFsEvents: false, // for linux support 77 | binaryInterval: delay_ * 5, 78 | interval: delay_//time 79 | 80 | , atomic: delay_ 81 | , awaitWriteFinish: { 82 | stabilityThreshold: delay_ * 1.2, 83 | pollInterval: delay_ *.5 84 | } 85 | }) 86 | 87 | let thiz = this 88 | this.watcher.on('add', async function (path) { 89 | await thiz.autoNT(path, 'a') 90 | }) 91 | this.watcher.on('change', async function (path) { 92 | await thiz.autoNT(path, 'c') 93 | }) 94 | }//() 95 | 96 | refreshBro() { 97 | MDevSrv.reloadServer.reload() 98 | } 99 | 100 | 101 | async autoNT(path_: string, wa:string) {//process 102 | log.info(wa) 103 | let path = Dirs.slash(path_) 104 | 105 | let p = path.lastIndexOf('/') 106 | let folder = '' 107 | let fn = path 108 | 109 | if (p > 0) { 110 | folder = path.substring(0, p) 111 | fn = path.substr(p + 1) 112 | } 113 | 114 | try { 115 | log.info('WATCHED1:', folder + '/' + fn) 116 | await this.mp.autoBake(folder, fn) 117 | await this.refreshBro() 118 | 119 | } catch (err) { 120 | log.warn(err) 121 | } 122 | }//() 123 | }//class 124 | 125 | export class MetaPro { 126 | mount: string 127 | b = new MBake() 128 | 129 | static folderProp = 'folder' 130 | 131 | static srcProp = 'src' 132 | static destProp = 'dest' 133 | 134 | 135 | constructor(mount) { 136 | this.mount = mount 137 | log.info('MetaPro', this.mount) 138 | } 139 | 140 | bake(dir: string): Promise { 141 | 142 | let folder = this.mount + '/' + dir 143 | log.info(folder) 144 | return this.b.bake(folder, 0) 145 | } 146 | 147 | itemize(dir: string): Promise { 148 | return this.b.itemizeNBake(this.mount + '/' + dir, 0) 149 | } 150 | 151 | css(dir: string):Promise { 152 | return new Sas().css(this.mount + '/' + dir) 153 | } 154 | 155 | ts(dir: string):Promise { 156 | const folder = this.mount + '/' + dir; 157 | const js = new MinJS(); 158 | return js.ts(folder); 159 | } 160 | 161 | // when you pass the file name, ex: watch 162 | async autoBake(folder__, file):Promise { 163 | const folder = Dirs.slash(folder__) 164 | 165 | const ext = file.split('.').pop() 166 | log.info('WATCHED2:', folder, ext) 167 | 168 | if (ext == 'scss' || ext == 'sass') // css 169 | return await this.css(folder) 170 | 171 | if (ext == 'ts') // ts 172 | return await this.ts(folder) 173 | 174 | if (ext == 'yaml') // bake and itemize 175 | return await this.itemize(folder) 176 | 177 | if (ext == 'md') 178 | return await this.bake(folder) 179 | 180 | if (ext == 'pug') { 181 | return await this.bake(folder) 182 | } 183 | return ('Cant process ' + ext) 184 | }//() 185 | 186 | }//class 187 | 188 | // Meta: ////////////////////// 189 | export class MDevSrv { 190 | static reloadServer 191 | 192 | constructor(dir, port, reloadPort?) { 193 | let app = express() 194 | log.info(dir, port) 195 | app.set('app port', port) 196 | const rport = Number(reloadPort) || 9856; 197 | reload(app, {verbose: false, port: rport}) 198 | .then((reloadServer_) => { 199 | MDevSrv.reloadServer = reloadServer_ 200 | log.info('reloadServer') 201 | }).catch(e => { 202 | log.info('==================e', e) 203 | }) 204 | 205 | app.set('views', dir) 206 | 207 | const bodyInterceptor = interceptor(function (req, res) { 208 | return { 209 | // Only HTML responses will be intercepted 210 | isInterceptable: function () { 211 | return /text\/html/.test(res.get('Content-Type')) 212 | }, 213 | intercept: function (body, send) { 214 | //log.info(' .') 215 | let $document = cheerio.load(body) 216 | $document('body').prepend('') 217 | send($document.html()) 218 | } 219 | } 220 | }) 221 | 222 | app.use(bodyInterceptor) 223 | 224 | app.use(express.static(dir)) 225 | app.listen(port, function () { 226 | log.info('dev srv ' + port) 227 | }) 228 | 229 | }//() 230 | }//class 231 | 232 | -------------------------------------------------------------------------------- /src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuition-dev/INTUITION/45a9199dc84756bbd509b63eba4139551bc0d7f7/src/logo.png -------------------------------------------------------------------------------- /src/mbake.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // All rights reserved by INTUITION.DEV | Cekvenich, licensed under LGPL 3.0 3 | import { Ver, MBake } from './lib/Base'; 4 | import { MinJS, Sas } from './lib/Extra'; 5 | const { Dirs } = require('agentg/lib/FileOpsExtra'); 6 | import { Wa } from './lib/Wa'; 7 | const { DownloadFrag, VersionNag, DownloadC } = require('agentg/lib/FileOpsExtra'); 8 | VersionNag.isCurrent('mbake', Ver.ver()).then(function (isCurrent_) { 9 | try { 10 | if (!isCurrent_) 11 | console.log('There is a newer version of mbake CLI, please update.'); 12 | else 13 | console.log('Current'); 14 | } 15 | catch (err) { 16 | console.log(err); 17 | } 18 | }); // 19 | // imports done ///////////////////////////////////////////// 20 | const cwd = process.cwd(); 21 | function version() { 22 | console.info('mbake CLI version: ' + Ver.ver()); 23 | } 24 | function help() { 25 | console.info(); 26 | console.info('mbake CLI version: ' + Ver.ver()); 27 | console.info(); 28 | console.info('Usage:'); 29 | console.info(' To process any_dir Pug to html recursively: mbake .'); 30 | console.info(' For local watcher and server: mbake -w .'); 31 | console.info(' Process SASS/SCSS file into css, requires style.yaml: mbake -s .'); 32 | console.info(' or path that has style.yaml, or any sub-folder under /style'); 33 | console.info(' Process .ts, .js or native Custom Elements(-custel) : mbake -t .'); 34 | console.info(' To process Pug and dat_i items to items.json: mbake -i .'); 35 | console.info(' or any sub-folder, where path is folder containing dat_i.yaml;'); 36 | console.info(' also does regular mbake of Pug'); 37 | console.info(' Download fragment to setup the app FW(framework): mbake -f .'); 38 | console.info(); 39 | console.info(' Download static (Pug) example: mbake --pug '); 40 | console.info(' For starter CRUD app: mbake -c'); 41 | console.info(' Note: . is current directory; or use any path instead of .'); 42 | console.info(' -------------------------------------------------------------'); 43 | console.info(); 44 | console.info(' mbakex CLI (extra) has more flags'); 45 | console.info(); 46 | console.info(' Full docs: http://www.INTUITION.DEV'); 47 | console.info(); 48 | } 49 | // args: ////////////////////////////////////////////////////////////////////////////////////////////////////// 50 | const optionDefinitions = [ 51 | { name: 'mbake', defaultOption: true }, 52 | { name: 'help', alias: 'h', type: Boolean }, 53 | { name: 'version', alias: 'v', type: Boolean }, 54 | { name: 'items', alias: 'i', type: Boolean }, 55 | { name: 'css', alias: 's', type: Boolean }, 56 | { name: 'MinJS', alias: 't', type: Boolean }, 57 | { name: 'frag', alias: 'f', type: Boolean }, 58 | { name: 'watcher', alias: 'w', type: Boolean }, 59 | { name: 'pug', type: Boolean }, 60 | { name: 'CRUD', alias: 'c', type: Boolean }, 61 | ]; 62 | const argsParsed = commandLineArgs(optionDefinitions); 63 | let arg = argsParsed.mbake; 64 | console.info(); 65 | function frag(arg) { 66 | new DownloadFrag(arg); 67 | } 68 | // get folder to be processed: /////////////////////////////////////////////////////////////////////////////////////////////////////// 69 | if (arg) { 70 | arg = Dirs.slash(arg); 71 | if (arg.startsWith('/')) { 72 | //do nothing, full path is arg 73 | } 74 | else if (arg.startsWith('..')) { // few cases to test 75 | arg = arg.substring(2); 76 | let d = cwd; 77 | d = Dirs.slash(d); 78 | // find offset 79 | let n = d.lastIndexOf('/'); 80 | d = d.substring(0, n); 81 | arg = d + arg; 82 | } 83 | else if (arg.startsWith('.')) { //cur 84 | arg = cwd; 85 | } 86 | else { // just plain, dir passed 87 | arg = cwd + '/' + arg; 88 | } // inner 89 | } //outer 90 | // //////////////////////////////////////////////////////////////////////////////////////////////// 91 | function pug() { 92 | new DownloadC('pug', arg).autoUZ(); 93 | console.info('Extracted Intro to Pug example'); 94 | } //() 95 | function unzipC() { 96 | new DownloadC('CRUD', arg).autoUZ(); 97 | console.info('Extracted an example CRUD app'); 98 | } 99 | function bake(arg) { 100 | let pro = new MBake().bake(arg, 0); 101 | pro.then(function (val) { 102 | console.log(val); 103 | }); 104 | } 105 | function itemize(arg) { 106 | let pro = new MBake().itemizeNBake(arg, 0); 107 | pro.then(function (val) { 108 | console.log(val); 109 | }); 110 | } 111 | function css(arg) { 112 | let pro = new Sas().css(arg); 113 | pro.then(function (val) { 114 | console.log(val); 115 | }); 116 | } 117 | function minJS(arg) { 118 | let min = new MinJS(); 119 | let pro = min.ts(arg); 120 | pro.then(function (val) { 121 | console.log(val); 122 | min.min(arg); 123 | }); 124 | } 125 | // start: ///////////////////////////////////////////////////////////////////////////////////// 126 | if (argsParsed.items) 127 | itemize(arg); 128 | else if (argsParsed.css) 129 | css(arg); 130 | else if (argsParsed.MinJS) 131 | minJS(arg); 132 | else if (argsParsed.frag) 133 | frag(arg); 134 | else if (argsParsed.pug) 135 | pug(); 136 | else if (argsParsed.CRUD) 137 | unzipC(); 138 | else if (argsParsed.version) 139 | version(); 140 | else if (argsParsed.help) 141 | help(); 142 | else if (argsParsed.watcher) 143 | Wa.watch(arg, argsParsed.port, argsParsed['reload-port']); 144 | else if (!arg) 145 | help(); 146 | else 147 | bake(arg); 148 | -------------------------------------------------------------------------------- /src/mbake.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // All rights reserved by INTUITION.DEV | Cekvenich, licensed under LGPL 3.0 3 | 4 | import commandLineArgs = require('command-line-args') 5 | 6 | import { Ver, MBake } from './lib/Base' 7 | import { MinJS, Sas } from './lib/Extra' 8 | const {Dirs} = require('agentg/lib/FileOpsExtra') 9 | import { Wa } from './lib/Wa' 10 | const { DownloadFrag, VersionNag, DownloadC } = require('agentg/lib/FileOpsExtra') 11 | 12 | VersionNag.isCurrent('mbake', Ver.ver()).then(function (isCurrent_: boolean) { 13 | try { 14 | if (!isCurrent_) 15 | console.log('There is a newer version of mbake CLI, please update.') 16 | else console.log('Current') 17 | } catch (err) { 18 | console.log(err) 19 | } 20 | })// 21 | 22 | // imports done ///////////////////////////////////////////// 23 | const cwd: string = process.cwd() 24 | 25 | function version() { 26 | console.info('mbake CLI version: ' + Ver.ver()) 27 | } 28 | 29 | function help() { 30 | console.info() 31 | console.info('mbake CLI version: ' + Ver.ver()) 32 | console.info() 33 | console.info('Usage:') 34 | console.info(' To process any_dir Pug to html recursively: mbake .') 35 | 36 | console.info(' For local watcher and server: mbake -w .') 37 | 38 | console.info(' Process SASS/SCSS file into css, requires style.yaml: mbake -s .') 39 | console.info(' or path that has style.yaml, or any sub-folder under /style') 40 | 41 | console.info(' Process .ts, .js or native Custom Elements(-custel) : mbake -t .') 42 | 43 | console.info(' To process Pug and dat_i items to items.json: mbake -i .') 44 | console.info(' or any sub-folder, where path is folder containing dat_i.yaml;') 45 | console.info(' also does regular mbake of Pug') 46 | 47 | console.info(' Download fragment to setup the app FW(framework): mbake -f .') 48 | console.info() 49 | console.info(' Download static (Pug) example: mbake --pug ') 50 | 51 | console.info(' For starter CRUD app: mbake -c') 52 | 53 | console.info(' Note: . is current directory; or use any path instead of .') 54 | console.info(' -------------------------------------------------------------') 55 | 56 | console.info() 57 | console.info(' mbakex CLI (extra) has more flags') 58 | console.info() 59 | console.info(' Full docs: http://www.INTUITION.DEV') 60 | 61 | console.info() 62 | } 63 | 64 | 65 | // args: ////////////////////////////////////////////////////////////////////////////////////////////////////// 66 | const optionDefinitions = [ 67 | { name: 'mbake', defaultOption: true }, 68 | 69 | { name: 'help', alias: 'h', type: Boolean }, 70 | { name: 'version', alias: 'v', type: Boolean }, 71 | 72 | { name: 'items', alias: 'i', type: Boolean }, 73 | { name: 'css', alias: 's', type: Boolean }, 74 | 75 | { name: 'MinJS', alias: 't', type: Boolean }, 76 | 77 | { name: 'frag', alias: 'f', type: Boolean }, 78 | 79 | { name: 'watcher', alias: 'w', type: Boolean }, 80 | 81 | { name: 'pug', type: Boolean }, 82 | { name: 'CRUD', alias: 'c', type: Boolean }, 83 | 84 | ] 85 | 86 | const argsParsed = commandLineArgs(optionDefinitions) 87 | let arg: string = argsParsed.mbake 88 | console.info() 89 | 90 | 91 | function frag(arg) { 92 | new DownloadFrag(arg) 93 | } 94 | 95 | // get folder to be processed: /////////////////////////////////////////////////////////////////////////////////////////////////////// 96 | if (arg) { 97 | arg = Dirs.slash(arg) 98 | 99 | if (arg.startsWith('/')) { 100 | //do nothing, full path is arg 101 | } else if (arg.startsWith('..')) { // few cases to test 102 | arg = arg.substring(2) 103 | let d = cwd 104 | d = Dirs.slash(d) 105 | // find offset 106 | let n = d.lastIndexOf('/') 107 | d = d.substring(0, n) 108 | arg = d + arg 109 | } else if (arg.startsWith('.')) {//cur 110 | arg = cwd 111 | } else { // just plain, dir passed 112 | arg = cwd + '/' + arg 113 | }// inner 114 | 115 | }//outer 116 | 117 | // //////////////////////////////////////////////////////////////////////////////////////////////// 118 | function pug() { 119 | new DownloadC('pug', arg).autoUZ() 120 | console.info('Extracted Intro to Pug example') 121 | }//() 122 | 123 | function unzipC() { 124 | new DownloadC('CRUD', arg).autoUZ() 125 | console.info('Extracted an example CRUD app') 126 | } 127 | 128 | function bake(arg) { 129 | let pro: Promise = new MBake().bake(arg, 0) 130 | 131 | pro.then(function (val) { 132 | console.log(val) 133 | }) 134 | } 135 | 136 | function itemize(arg) { 137 | let pro: Promise = new MBake().itemizeNBake(arg, 0) 138 | 139 | pro.then(function (val) { 140 | console.log(val) 141 | }) 142 | } 143 | 144 | function css(arg) { 145 | let pro: Promise = new Sas().css(arg) 146 | 147 | pro.then(function (val) { 148 | console.log(val) 149 | }) 150 | } 151 | 152 | function minJS(arg) { 153 | let min = new MinJS() 154 | let pro: Promise = min.ts(arg) 155 | pro.then(function (val) { 156 | console.log(val) 157 | min.min(arg) 158 | }) 159 | 160 | } 161 | 162 | // start: ///////////////////////////////////////////////////////////////////////////////////// 163 | if (argsParsed.items) 164 | itemize(arg) 165 | else if (argsParsed.css) 166 | css(arg) 167 | else if (argsParsed.MinJS) 168 | minJS(arg) 169 | else if (argsParsed.frag) 170 | frag(arg) 171 | else if (argsParsed.pug) 172 | pug() 173 | else if (argsParsed.CRUD) 174 | unzipC() 175 | else if (argsParsed.version) 176 | version() 177 | else if (argsParsed.help) 178 | help() 179 | else if (argsParsed.watcher) 180 | Wa.watch(arg, argsParsed.port, argsParsed['reload-port']); 181 | else if (!arg) 182 | help() 183 | else 184 | bake(arg) 185 | 186 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mbake", 3 | "preferGlobal": true, 4 | "version": "8.5.4", 5 | "description": "mbake CLI is open source and extensible low-code productivity bundler/builder that leverages Pug and JAMstack.", 6 | "bin": "mbake.js", 7 | "license": "LGPL-3.0-only", 8 | "author": "http://www.INTUITION.DEV", 9 | "homepage": "http://www.INTUITION.DEV", 10 | "repository": { 11 | "type": "git", 12 | "url": "http://git.metabake.net" 13 | }, 14 | "keywords": [ 15 | "pug", 16 | "static gen", 17 | "low-code", 18 | "CRUD" 19 | ], 20 | "engines": { 21 | "node": ">=14.0.0" 22 | }, 23 | "dependencies": { 24 | "agentg": ">=2.5.2", 25 | "autoprefixer": "9.8.6", 26 | "cheerio": ">=1.0.0-rc.3", 27 | "chokidar": ">=3.4.3", 28 | "command-line-args": ">=5.1.1", 29 | "express-interceptor": ">=1.2.0", 30 | "filehound": ">=1.17.4", 31 | "find-up": ">=5.0.0", 32 | "html-minifier": ">=4.0.0", 33 | "javascript-obfuscator": ">=2.6.1", 34 | "markdown-it": ">=12.0.0", 35 | "markdown-it-container": ">=3.0.0", 36 | "markdown-it-imsize": ">=2.0.1", 37 | "node-sass": ">=4.14.1", 38 | "open": ">=7.3.0", 39 | "postcss": "7.0.34", 40 | "pug": ">=3.0.0", 41 | "reload": ">=3.1.1", 42 | "strip-css-comments": ">=4.1.0", 43 | "terser": ">=5.3.5", 44 | "typescript": ">=4.0.3" 45 | }, 46 | "browserslist": [ 47 | "> 1%", 48 | "not ie < 14" 49 | ], 50 | "devDependencies": { 51 | "@types/express": ">=4.17.8", 52 | "@types/node": ">=14.11.10", 53 | "dompurify": ">=2.1.1", 54 | "legit": ">=1.0.9", 55 | "lodash": ">=4.17.20", 56 | "string-template": ">=1.0.0", 57 | "he": "^1.2.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/publish.sh: -------------------------------------------------------------------------------- 1 | # zip up the sample apps, update Base.ts version 2 | # ncu -u 3 | tsc 4 | npm publish 5 | #sudo npm i -g mbake --unsafe-perm=true --allow-root 6 | 7 | # if changed node version do this: 8 | 9 | mbake 10 | npm i -g mbake 11 | mbake 12 | 13 | # also update https://github.com/metabake/mBakeCli/blob/master/docs/versions.yaml 14 | 15 | # find . -type f -name 'package-lock.json' -------------------------------------------------------------------------------- /src/release.txt: -------------------------------------------------------------------------------- 1 | base lib 2 | pacakge.json 3 | change notes 4 | versions 5 | 6 | Note: if changed node version you may need to do this first: `yarn global remove mbake` 7 | or /usr/local/share/.config/yarn/global/node_modules 8 | 9 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "esModuleInterop" : true, 8 | "allowSyntheticDefaultImports": true, 9 | 10 | "lib": ["ES2018"], 11 | 12 | "strictBindCallApply": true, 13 | "noImplicitThis": true, 14 | 15 | "removeComments": false, 16 | "outDir": ".", 17 | "baseUrl": ".", 18 | "paths": { 19 | "*": ["node_modules/*"] 20 | } 21 | }, 22 | 23 | "include": [ 24 | "**/*.ts" 25 | ], 26 | "exclude": [ 27 | "node_modules", 28 | "webApp" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /src/x.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clear 3 | tsc 4 | 5 | node mbake.js 6 | -------------------------------------------------------------------------------- /versions.yaml: -------------------------------------------------------------------------------- 1 | docs/versions.yaml --------------------------------------------------------------------------------