├── .babelrc ├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── build-runtime.json ├── docs ├── decorators │ ├── action.md │ ├── component.md │ ├── model.md │ ├── on.md │ ├── pubsub.md │ ├── style.md │ └── view.md └── libs │ ├── customelement.md │ ├── eventhandler.md │ ├── pubsub.md │ ├── router.md │ ├── stylesheet.md │ └── view.md ├── gulpfile.js ├── jspm.browser.js ├── jspm.config.js ├── karma.conf.js ├── lerna.json ├── mocha.opts ├── package.json ├── packages ├── app-decorators-cli-deps │ ├── appdec.json │ └── package.json ├── app-decorators-element-to-function │ ├── README.md │ ├── index.js │ └── package.json ├── app-decorators-simple-it │ ├── .babelrc │ ├── package.json │ └── src │ │ └── index.js ├── app-decorators-todomvc │ ├── .babelrc │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── assets │ │ └── todomvc-app-decorators.png │ ├── gulpfile.js │ ├── index.html │ ├── index.js │ ├── jspm.config.js │ ├── package.json │ └── src │ │ ├── app.js │ │ ├── dom.js │ │ ├── todo-item.js │ │ ├── todo-list.js │ │ ├── todo-new.js │ │ └── utils.js ├── app-decorators │ ├── .babelrc │ ├── bootstrap.js │ ├── component.tpl │ ├── html.tpl │ ├── jspm.config.js │ ├── jspm.config.json │ ├── package.json │ └── server.js ├── babel-plugin-app-decorators-action-bundle │ └── README.md ├── babel-plugin-app-decorators-component-register │ ├── .babelrc │ ├── CHANGELOG.md │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.js │ └── test │ │ └── index.js ├── babel-plugin-app-decorators-component │ ├── .babelrc │ ├── CHANGELOG.md │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── package.json │ ├── src │ │ ├── elements.js │ │ └── index.js │ └── test │ │ └── index.js ├── babel-plugin-app-decorators-element-to-function │ ├── .babelrc │ ├── README.md │ ├── package.json │ ├── src │ │ ├── elements.js │ │ └── index.js │ └── test │ │ └── index.js ├── babel-plugin-app-decorators-style-precompile │ ├── .babelrc │ ├── README.md │ ├── package.json │ └── src │ │ ├── index.js │ │ └── index.test.js ├── babel-plugin-app-decorators-view-precompile │ ├── .babelrc │ ├── CHANGELOG.md │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.js │ └── test │ │ └── index.js ├── babel-preset-app-decorators │ ├── .babelrc │ ├── .editorconfig │ ├── Makefile │ ├── README.md │ ├── package.json │ └── src │ │ └── index.js └── postcss-parse-atrule-events │ ├── .babelrc │ ├── README.md │ ├── package.json │ └── src │ ├── index.js │ └── index.test.js ├── src ├── apps │ ├── pubsub.js │ └── router.js ├── bootstrap.js ├── configs │ ├── elements.js │ ├── route.config.client.js │ └── window-events.js ├── datas │ ├── init-maps.js │ └── polyfills.js ├── decorators │ ├── action.js │ ├── component.js │ ├── model.js │ ├── modelpool.js │ ├── on.js │ ├── pubsub.js │ ├── style.js │ └── view.js ├── helpers │ ├── browser-detect.js │ ├── delay.js │ ├── extract-dom-properties.js │ ├── guid.js │ ├── history.back-forward-and-wait.js │ ├── jquery.click-and-wait.js │ ├── lazyPolyfillLoader.js │ ├── namespace.js │ ├── queryString.js │ ├── string.js │ └── trigger.js ├── index.js └── libs │ ├── customelement.js │ ├── dependencies.js │ ├── element-to-function.js │ ├── eventhandler.js │ ├── pubsub.js │ ├── random-storage.js │ ├── router.js │ ├── router.runtime.js │ ├── stylesheet.js │ └── view.js └── test ├── .babelrc ├── decorators ├── action.spec.js ├── component.spec.js ├── on.spec.js ├── pipe.spec.js ├── style.spec.js └── view.spec.js ├── fixture ├── server-styles-4000.js └── styles │ ├── test-1.css │ ├── test-2.css │ └── test-3.css ├── helpers ├── extract-dom-properties.spec.js ├── guid.spec.js ├── namespace.spec.js ├── querString.spec.js └── remove-gutter.js ├── imports ├── app.spec.js └── testcomponent.js ├── libs ├── customelement.spec.js ├── element-to-function.spec.js ├── eventhandler.spec.js ├── router.spec.js ├── stylesheet.spec.js └── view.spec.js └── mocks ├── event.js └── location.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { 4 | "modules" : "systemjs" 5 | }], 6 | "app-decorators" 7 | ] 8 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = tab 3 | indent_size = 4 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | ideas 3 | .DS_Store 4 | docs/.DS_Store 5 | node_modules 6 | jspm_packages 7 | packages/app-decorators/src 8 | packages/app-decorators/test 9 | packages/app-decorators/runtime.js 10 | packages/app-decorators-todomvc/dist/ 11 | packages/**/lib/ 12 | packages/**/tmp/ 13 | packages/**/node_modules 14 | packages/**/jspm_packages -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Serkan Sipahi 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. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | @echo "" 3 | @echo "Available Targets:" 4 | @echo "" 5 | @echo " make install" 6 | @echo " make compile" 7 | @echo " make test" 8 | @echo " make clean" 9 | @echo " make publish (npm)" 10 | @echo "" 11 | @echo " lerna-test" 12 | @echo " lerna-clean" 13 | @echo " lerna-publish" 14 | @echo " lerna-publish-version version=1.0.0" 15 | @echo " lerna-updated" 16 | @echo " lerna-ncu" 17 | @echo " lerna-ncu-update" 18 | @echo " lerna-npm-install-save module=underscore" 19 | @echo " lerna-npm-install-save-dev module=app-decorators" 20 | @echo "" 21 | 22 | install: jspm-install-packages prepare-compile gulp-compile 23 | 24 | compile: prepare-compile gulp-compile-watch 25 | 26 | publish: lerna-publish 27 | 28 | node_modules: 29 | npm install --no-package-lock 30 | 31 | jspm-install-packages: 32 | $(jspm) install 33 | 34 | gulp-compile: 35 | $(gulp) compile; 36 | 37 | gulp-compile-watch: 38 | $(gulp) compile-watch; 39 | 40 | lerna-init: 41 | lerna init $(set); 42 | 43 | lerna-publish-version: 44 | lerna publish --repo-version $(version) --exact --force-publish=* $(set); 45 | 46 | lerna-publish: 47 | make bundle-runtime && lerna publish --exact --force-publish=* $(set); 48 | 49 | lerna-updated: 50 | lerna updated $(set); 51 | 52 | lerna-ncu: 53 | lerna exec -- ncu; 54 | 55 | lerna-ncu-update: 56 | lerna exec -- ncu -u; 57 | 58 | lerna-npm-install-save: 59 | lerna exec -- npm install $(module) --save; 60 | 61 | lerna-npm-install-save-dev: 62 | lerna exec -- npm install $(module) --save-dev; 63 | 64 | lerna-clean: 65 | command -v lerna >/dev/null && lerna clean --yes; 66 | 67 | lerna-test: 68 | lerna run test 69 | 70 | lerna-bootstrap: 71 | lerna bootstrap -- --no-package-lock; 72 | 73 | bundle-runtime: 74 | $(jspm) bundle app-decorators packages/app-decorators/runtime.js \ 75 | --config build-runtime.json \ 76 | --minify \ 77 | --skip-source-maps; 78 | 79 | test: 80 | $(karma) start 81 | 82 | clean: 83 | rm -rf node_modules jspm_packages; \ 84 | make clean-package-compiled; 85 | 86 | clean-package-compiled: 87 | rm -rf packages/*/{lib,dist}; \ 88 | rm -rf packages/app-decorators/{build,dist,tmp,src,test,node_modules,jspm_packages}; 89 | 90 | start-asset-css-server: 91 | node test/fixture/server-styles-4000.js 92 | 93 | prepare-compile: 94 | rm -rf packages/app-decorators/{lib,src,test,tmp}; \ 95 | mkdir -p packages/app-decorators/tmp; \ 96 | cp jspm.browser.js jspm.config.js packages/app-decorators/tmp; \ 97 | make start-asset-css-server; 98 | 99 | fix-nested-node_modules: 100 | rm -rf packages/app-decorators/node_modules/node_modules; 101 | 102 | jspm = ./node_modules/.bin/jspm 103 | karma = ./node_modules/.bin/karma 104 | gulp = ./node_modules/.bin/gulp 105 | 106 | .PHONY: install test clean node_modules jspm-install-packages compile test lerna-init lerna-publish; 107 | MAKEFLAGS = -s 108 | -------------------------------------------------------------------------------- /build-runtime.json: -------------------------------------------------------------------------------- 1 | { 2 | "transpiler": false, 3 | "packageConfigPaths": [ 4 | "npm:@*/*.json", 5 | "npm:*.json", 6 | "github:*/*.json" 7 | ], 8 | "paths": { 9 | "github": "jspm_packages/github/", 10 | "npm:": "jspm_packages/npm/", 11 | "src/": "lib/", 12 | "test/": "test/", 13 | "app-decorators/": "packages/app-decorators/" 14 | }, 15 | "map": { 16 | "app-decorators": "app-decorators/src/index", 17 | "app-decorators/src/bootstrap": "app-decorators/src/bootstrap", 18 | "app-decorators/src/libs/customelement": "app-decorators/src/libs/customelement", 19 | "app-decorators/src/libs/random-storage": "app-decorators/src/libs/random-storage", 20 | "app-decorators/src/libs/element-to-function": "app-decorators/src/libs/element-to-function" 21 | }, 22 | "packages": { 23 | "packages": { 24 | "defaultExtension": "js" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /docs/decorators/action.md: -------------------------------------------------------------------------------- 1 | ### @action 2 | 3 | ```js 4 | import { component, action, view } from 'app-decorators'; 5 | 6 | 7 | @view(` 8 | ||| 9 | `) 10 | 11 | @component() 12 | class Sidebar { 13 | 14 | /** 15 | * Sidebar 16 | * @param {Event} event 17 | * @param {object} params 18 | */ 19 | @action('?sidebar={{state}}') sidebar({ event, params, search, hash }){ 20 | 21 | console.log( 22 | event, // Event 23 | event.target, // 61 | 62 | 63 | 64 | @component 65 | 66 | 67 | 68 | 69 |

Hello World

70 |
71 | 72 | 73 | ``` 74 | or directly with javascript 75 | ```js 76 | 77 | // creating domNode 78 | let item1 = Item.create(); 79 | 80 | console.log(item1 instanceof HTMLElement); // log true 81 | 82 | document.body.appendChild(item1); 83 | 84 | // will rendered: 85 | /** 86 | 87 | 88 |

Hello World

89 |
90 | 91 | **/ 92 | 93 | // it will blink every 500ms 94 | item1.blink(500); 95 | 96 | // or if node already exists in dom 97 | document.querySelector('com-item').blink(500); 98 | 99 | // it will stop blinking after 20 seconds 100 | setTimeout(() => { 101 | item1.stop(); 102 | }, 20000); 103 | ``` 104 | 105 | ##### Advantage usage: 106 | 107 | ```js 108 | // its also possible to pass your own properties 109 | let item2 = Item.create({ 110 | prefix: 'Whats', 111 | suffix: 'up!', 112 | }); 113 | 114 | document.body.appendChild(item2); 115 | 116 | // will rendered: 117 | /** 118 | 119 | 120 |

Whats up!

121 |
122 | 123 | **/ 124 | 125 | item2.blink(300); 126 | ``` 127 | ##### Settings: 128 | ```js 129 | import { component } from 'app-decorators'; 130 | 131 | @component({ 132 | extends: 'img' 133 | }) 134 | class Coffee { 135 | created({ art }){ 136 | this.src = 'some/${art}/pic.png'; 137 | } 138 | } 139 | 140 | /** 141 | * You have access for the passed arguments over 142 | * created({ art } or over this.art 143 | */ 144 | let coffee = Coffee.create({art: 'espresso'}); 145 | 146 | console.log(coffee instanceof HTMLImageElement) // log true 147 | 148 | document.body.appendChild(coffee); 149 | 150 | // will rendered 151 | /** 152 | 153 | **/ 154 | 155 | ``` 156 | ##### Lifecycle of Component (Custom Elements): 157 | ```js 158 | import { component } from 'app-decorators'; 159 | 160 | class Bean { 161 | origin = 'africa'; 162 | } 163 | 164 | @component({ 165 | extends: 'img' 166 | }) 167 | 168 | // and its possible to extends from other class 169 | class Coffee extends Bean { 170 | 171 | /** 172 | * You can add your custom properties and methods. But be careful when choosing the name 173 | * of property/method because "this" has a direct reference to dom instance 174 | */ 175 | foo = 'Hello'; 176 | bar = 'World'; 177 | 178 | someDoSomethingMethod(){ 179 | this.classList.add(this.origin); // see in "class Bean" whats origin is 180 | } 181 | 182 | /** 183 | * CustomElements Callbacks 184 | */ 185 | 186 | // will called if element is created 187 | created(){ 188 | 189 | } 190 | 191 | // will called if element is in document 192 | attached(){ 193 | 194 | } 195 | 196 | // will called if any attribute changed or added to element 197 | attributeChanged(oldValue, newValue){ 198 | 199 | } 200 | 201 | // will called if element is detached/removed from document 202 | detached(){ 203 | 204 | } 205 | } 206 | ``` 207 | -------------------------------------------------------------------------------- /docs/decorators/model.md: -------------------------------------------------------------------------------- 1 | ### @model [ in progress / idea ] 2 | 3 | ```js 4 | // components/item.js 5 | import { component, view, model } from 'app-decorators'; 6 | 7 | @view(` 8 |
{{name}}
9 |
{{city}}
10 |
{{country}}
11 | `) 12 | 13 | @component() 14 | class Item { 15 | 16 | @model.attr @view.bind name; 17 | @model.attr @view.bind city; 18 | @model.attr @view.bind country = 'Turkey'; 19 | 20 | created(){ 21 | this.name = 'Serkan'; 22 | this.city = 'Istanbul'; 23 | } 24 | 25 | @model('read:name') read( value ) { 26 | this.name = `${value} [ read ]`; 27 | } 28 | @model('update:name') updateName( value ) { 29 | this.name = `${value} [ update ]`; 30 | } 31 | @model('update:city') updateCity( value ) { 32 | this.city = `${value} [ update ]`; 33 | } 34 | @model('delete:country') delete( ) { 35 | this.country = '-'; 36 | } 37 | 38 | } 39 | 40 | let item = Item.instance(); 41 | document.body.appendChild(item); 42 | 43 | /** 44 | // output 45 | 46 |
Serkan
47 |
Istanbul
48 |
Turkey
49 |
50 | */ 51 | 52 | ``` 53 | ```js 54 | // ...somewhere in another file 55 | 56 | let item = document.querySelector('com-item'); 57 | item.model.set('name', 'Ayil'); 58 | 59 | /** 60 | // output 61 | 62 |
Ayil [ update ]
63 |
Istanbul
64 |
Turkey
65 |
66 | */ 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/decorators/on.md: -------------------------------------------------------------------------------- 1 | ### @on 2 | 3 | ```js 4 | import { component, view, on } from 'app-decorators'; 5 | 6 | @view(` 7 |
add
8 |
edit
9 |
delete
10 | `) 11 | 12 | @component() 13 | class Item { 14 | 15 | @on('click .add') addItem( event ){ 16 | console.log('on click .add'); 17 | } 18 | @on('click .edit') editItem( event ){ 19 | console.log('on click .edit'); 20 | } 21 | @on('click .delete') deleteItem( event ){ 22 | console.log('on click .add'); 23 | } 24 | 25 | // bind global events 26 | @on('resize', window) onResize(event){ 27 | 28 | } 29 | 30 | } 31 | 32 | let item = Item.create(); 33 | document.body.appendChild(item); 34 | ``` 35 | ```html 36 | 37 | 38 | 39 |
add
40 |
edit
41 |
delete
42 |
43 | 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/decorators/pubsub.md: -------------------------------------------------------------------------------- 1 | ## @pubsub 2 | 3 | ## Usage 4 | 5 | documentation coming soon 6 | -------------------------------------------------------------------------------- /docs/decorators/style.md: -------------------------------------------------------------------------------- 1 | ### @style 2 | 3 | ## Usage 4 | 5 | documentation coming soon 6 | -------------------------------------------------------------------------------- /docs/decorators/view.md: -------------------------------------------------------------------------------- 1 | ### @view 2 | 3 | ##### counter.js 4 | ```js 5 | import { component, view, on } from 'app-decorators'; 6 | 7 | @view(` 8 |

{{header}}

9 |
{{count}}
10 |
11 | + 12 | - 13 |
14 | `) 15 | 16 | @component() 17 | class Counter { 18 | 19 | @view.bind count; 20 | 21 | @on('click .up') onClickUp() { 22 | ++this.count 23 | } 24 | 25 | @on('click .down') onClickUp() { 26 | --this.count 27 | } 28 | } 29 | ``` 30 | 31 | ##### create in html: 32 | 33 | ```html 34 | 35 | ``` 36 | 37 | ##### create directly with javascript 38 | ```html 39 | 40 | let counter = Counter.create({ 41 | header: 'My Counter', 42 | count: 0, 43 | }); 44 | 45 | document.body.appendChild(counter); 46 | ``` 47 | 48 | ##### will render: 49 | ```html 50 | 51 |

My Counter

52 |
0
53 |
54 | + 55 | - 56 |
57 |
58 | ``` 59 | --- 60 | 61 | ##### using slot: 62 | ```js 63 | import { component, view, on } from 'app-decorators'; 64 | 65 | @view(` 66 |

67 |
{{count}}
68 |
69 | + 70 | - 71 |
72 | `) 73 | 74 | @component() 75 | class Counter { 76 | 77 | @view.bind count; 78 | 79 | @on('click .up') onClickUp() { 80 | ++this.count 81 | } 82 | 83 | @on('click .down') onClickUp() { 84 | --this.count 85 | } 86 | } 87 | ``` 88 | ```html 89 | 90 | My Counter 91 | 92 | ``` 93 | 94 | ##### will render: 95 | ```html 96 | 97 |

98 | My Counter 99 |

100 |
0
101 |
102 | + 103 | - 104 |
105 |
106 | ``` 107 | 108 | --- 109 | 110 | ##### it also possible nested components for complex structures: 111 | 112 | ```js 113 | 114 | // myparent.js 115 | @component({ 116 | name: 'my-parent', 117 | }) 118 | @view(` 119 |
120 |
121 |
122 | 123 |
124 | `) 125 | class MyParent { 126 | } 127 | 128 | //mychild.js 129 | @component({ 130 | name: 'my-child', 131 | }) 132 | @view(` 133 | 140 | `) 141 | class MySister { 142 | } 143 | 144 | // mysister.js 145 | @component({ 146 | name: 'my-sister', 147 | }) 148 | @view('

sister

') 149 | class MySister { 150 | } 151 | 152 | ``` 153 | 154 | #### then slot the components together 155 | ```html 156 | 157 | 158 | 159 |

Hello World 160 | 161 | 162 | 163 | ``` 164 | ##### will render: 165 | ```html 166 | 167 |

168 |
169 |
170 | 171 | 172 | 173 |
    174 |
  • One
  • 175 |
  • Two
  • 176 |
  • 177 | 178 | 179 |

    Hello World

    180 |

    sister

    181 |
    182 |
    183 |
  • 184 |
185 |
186 |
187 |
188 |
189 |
190 | ``` -------------------------------------------------------------------------------- /docs/libs/customelement.md: -------------------------------------------------------------------------------- 1 | ## libs/customelement.js 2 | 3 | ## Usage 4 | 5 | documentation coming soon 6 | -------------------------------------------------------------------------------- /docs/libs/eventhandler.md: -------------------------------------------------------------------------------- 1 | ## libs/eventhandler.js 2 | 3 | ## Usage 4 | 5 | documentation coming soon 6 | -------------------------------------------------------------------------------- /docs/libs/pubsub.md: -------------------------------------------------------------------------------- 1 | ## libs/pubsub.js 2 | 3 | ## Usage 4 | 5 | documentation coming soon 6 | -------------------------------------------------------------------------------- /docs/libs/stylesheet.md: -------------------------------------------------------------------------------- 1 | ## libs/stylesheet.js 2 | 3 | ## Usage 4 | 5 | documentation coming soon -------------------------------------------------------------------------------- /docs/libs/view.md: -------------------------------------------------------------------------------- 1 | ## libs/view.js 2 | 3 | ## Usage 4 | 5 | documentation coming soon 6 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const sourcemaps = require('gulp-sourcemaps'); 3 | const babel = require('gulp-babel'); 4 | const changed = require('gulp-changed'); 5 | 6 | gulp.task('compile:src', () => { 7 | return gulp.src('src/**/*.js') 8 | .pipe(changed('packages/app-decorators/src')) 9 | .pipe(sourcemaps.init()) 10 | .pipe(babel()) 11 | .pipe(sourcemaps.write('.')) 12 | .pipe(gulp.dest('packages/app-decorators/src')) 13 | .pipe(gulp.dest('packages/app-decorators/lib')); 14 | }); 15 | 16 | gulp.task('compile:test', () => { 17 | return gulp.src('test/**/*.js') 18 | .pipe(changed('packages/app-decorators/test')) 19 | .pipe(sourcemaps.init()) 20 | .pipe(babel()) 21 | .pipe(sourcemaps.write('.')) 22 | .pipe(gulp.dest('packages/app-decorators/test')); 23 | }); 24 | 25 | gulp.task('watch', function() { 26 | gulp.watch( 27 | ['src/**/*.js', 'test/**/*.js'], 28 | ['compile:src', 'compile:test'] 29 | ); 30 | }); 31 | 32 | gulp.task('compile', ['compile:src', 'compile:test']); 33 | gulp.task('compile-watch', ['compile:src', 'compile:test', 'watch']); 34 | -------------------------------------------------------------------------------- /jspm.browser.js: -------------------------------------------------------------------------------- 1 | SystemJS.config({ 2 | paths: { 3 | 'github:': 'jspm_packages/github/', 4 | 'npm:': 'jspm_packages/npm/' 5 | } 6 | }); 7 | -------------------------------------------------------------------------------- /jspm.config.js: -------------------------------------------------------------------------------- 1 | SystemJS.config({ 2 | transpiler: false, 3 | map: { 4 | "app-decorators": "src/index", 5 | "app-decorators/src/bootstrap": "src/bootstrap", 6 | "app-decorators/src/libs/customelement": "src/libs/customelement", 7 | "app-decorators/src/libs/random-storage": "src/libs/random-storage", 8 | "app-decorators/src/libs/element-to-function": "src/libs/element-to-function" 9 | }, 10 | packages: { 11 | "src": { 12 | "defaultExtension": "js" 13 | }, 14 | "test": { 15 | "defaultExtension": "js" 16 | }, 17 | "node_modules": { 18 | "defaultExtension": "js" 19 | } 20 | } 21 | }); 22 | 23 | SystemJS.config({ 24 | packageConfigPaths: [ 25 | "npm:@*/*.json", 26 | "npm:*.json", 27 | "github:*/*.json" 28 | ], 29 | }); 30 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | 3 | config.set({ 4 | basePath: './packages/app-decorators', 5 | 6 | frameworks: [ 7 | 'jspm', 8 | 'mocha', 9 | 'sinon', 10 | 'should', 11 | ], 12 | plugins: [ 13 | 'karma-chrome-launcher', 14 | 'karma-firefox-launcher', 15 | 'karma-mocha', 16 | 'karma-sinon', 17 | 'karma-should', 18 | 'karma-jspm', 19 | 'karma-safari-launcher', 20 | 'karma-opera-launcher', 21 | ], 22 | browsers: [ 23 | 'Chrome', 24 | 'ChromeCanary', 25 | 'Firefox', 26 | 'Safari', 27 | 'Opera', 28 | //'FirefoxEnableWebComponents', 29 | ], 30 | customLaunchers: { 31 | FirefoxEnableWebComponents: { 32 | base: 'Firefox', 33 | prefs: { 34 | 'dom.webcomponents.enabled': true 35 | } 36 | }, 37 | }, 38 | files: [ 39 | {pattern: '**/*.js.map', included: false}, 40 | ], 41 | client: { 42 | mocha: { 43 | opts: './mocha.opts', 44 | timeout: 60000, 45 | }, 46 | }, 47 | jspm: { 48 | 49 | browser: "tmp/jspm.browser.js", 50 | config: "tmp/jspm.config.js", 51 | 52 | loadFiles: [ 53 | // includes regenerator-runtime: useful for async await 54 | 'jspm_packages/npm/babel-polyfill*/dist/polyfill.js', 55 | 56 | 'test/decorators/*spec.js', 57 | 'test/libs/*spec.js', 58 | 'test/helpers/*spec.js', 59 | 'test/imports/*.js', 60 | 'test/mocks/*.js', 61 | 62 | '!test/libs/pipe.spec.js', 63 | ], 64 | serveFiles: [ 65 | // internal libs/files 66 | 'src/bootstrap.js', 67 | 'src/index.js', 68 | 'src/decorators/*.js', 69 | 'src/libs/*.js', 70 | 'src/helpers/*.js', 71 | 'src/configs/*.js', 72 | 'src/apps/*.js', 73 | 'src/datas/*.js', 74 | 75 | 'node_modules/@webcomponents/shadydom/shadydom.min.js', 76 | 'node_modules/core-js/**/*.js', 77 | 'node_modules/handlebars/dist/**/*.js', 78 | 'node_modules/webcomponents.js/webcomponents-lite.js', 79 | 'node_modules/named-js-regexp/lib/named-js-regexp.js', 80 | 'node_modules/extend/index.js', 81 | 'node_modules/underscore/underscore-min.js', 82 | ] 83 | }, 84 | autoWatch: true, 85 | singleRun: false, 86 | port: 9876, 87 | colors: true, 88 | logLevel: config.LOG_INFO, 89 | }); 90 | 91 | if(process.env.TRAVIS){ 92 | config.browsers = ['Chrome_travis_ci']; 93 | } 94 | 95 | }; 96 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.8.252", 3 | "command": { 4 | "publish": { 5 | "ignoreChanges": [ 6 | "test", 7 | "tmp", 8 | "debug", 9 | "node_modules", 10 | "jspm_packages", 11 | "CHANGELOG.md", 12 | "Makefile", 13 | ".babelrc", 14 | ".idea" 15 | ] 16 | } 17 | }, 18 | "packages": [ 19 | "./", 20 | "packages/*" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 99999 2 | --ui bdd -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-decorators-core", 3 | "version": "0.8.252", 4 | "description": "Collection of useful ES7 Decorators, writtin in ES6, that can be used for building webapps", 5 | "main": "lib/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/SerkanSipahi/app-decorators.git" 9 | }, 10 | "author": "Serkan Sipahi", 11 | "license": "MIT", 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "homepage": "https://github.com/SerkanSipahi/app-decorators#readme", 16 | "scripts": { 17 | "test": "echo 'no tests required'" 18 | }, 19 | "keywords": [ 20 | "es6", 21 | "es7", 22 | "es2015", 23 | "es2016", 24 | "babel", 25 | "decorators", 26 | "component", 27 | "model", 28 | "view", 29 | "controller", 30 | "eventlistener", 31 | "router", 32 | "convention", 33 | "webapp", 34 | "mixin" 35 | ], 36 | "devDependencies": { 37 | "@types/mocha": "^5.2.1", 38 | "@types/sinon": "^5.0.1", 39 | "@types/systemjs": "^0.20.6", 40 | "@webcomponents/shadycss": "1.2.1", 41 | "@webcomponents/shadydom": "1.0.14", 42 | "babel-cli": "6.26.0", 43 | "babel-plugin-external-helpers": "6.22.0", 44 | "babel-plugin-transform-async-to-generator": "6.24.1", 45 | "babel-polyfill": "6.26.0", 46 | "babel-preset-app-decorators": "0.8.252", 47 | "babel-preset-es2015": "6.24.1", 48 | "core-js": "2.5.7", 49 | "dom4": "2.0.1", 50 | "express": "4.16.3", 51 | "extend": "3.0.1", 52 | "gulp": "3.9.1", 53 | "gulp-babel": "6.1.2", 54 | "gulp-changed": "3.2.0", 55 | "gulp-concat": "2.6.1", 56 | "gulp-replace": "1.0.0", 57 | "gulp-sourcemaps": "2.6.4", 58 | "handlebars": "4.0.11", 59 | "jspm": "0.17.0-beta.45", 60 | "karma": "2.0.2", 61 | "karma-chrome-launcher": "2.2.0", 62 | "karma-firefox-launcher": "1.1.0", 63 | "karma-jspm": "2.2.3", 64 | "karma-mocha": "1.3.0", 65 | "karma-mocha-reporter": "2.2.5", 66 | "karma-opera-launcher": "1.0.0", 67 | "karma-safari-launcher": "1.0.0", 68 | "karma-should": "1.0.0", 69 | "karma-sinon": "1.0.5", 70 | "lerna": "3.0.0-beta.20", 71 | "lighthouse": "2.9.4", 72 | "mocha": "5.2.0", 73 | "named-js-regexp": "1.3.3", 74 | "should": "13.2.1", 75 | "sinon": "5.1.1", 76 | "underscore": "1.9.1", 77 | "webcomponents.js": "0.7.24" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /packages/app-decorators-cli-deps/appdec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{name}}", 3 | "version": "0.0.1", 4 | "main": "lib/index.js", 5 | "dependencies": { 6 | "app-decorators": "{{version}}" 7 | }, 8 | "devDependencies": { 9 | "babel-cli": "6.24.1", 10 | "jspm": "0.17.0-beta.45", 11 | "express": "4.15.0" 12 | }, 13 | "appdec": { 14 | "name": "{{name}}", 15 | "version": "{{version}}" 16 | }, 17 | "jspm": { 18 | "dependencies": {}, 19 | "devDependencies": {} 20 | } 21 | } -------------------------------------------------------------------------------- /packages/app-decorators-cli-deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-decorators-cli-deps", 3 | "version": "0.8.252", 4 | "description": "app-decorators-cli-deps", 5 | "author": "Serkan Sipahi", 6 | "license": "MIT", 7 | "homepage": "https://github.com/SerkanSipahi/app-decorators-todomvc#readme", 8 | "scripts": { 9 | "prepublish": "echo 'no prepublish required'", 10 | "test": "echo 'no tests required'" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/app-decorators-element-to-function/README.md: -------------------------------------------------------------------------------- 1 | ## app-decorators-element-to-function 2 |

3 | Dependency Status 4 | devDependency Status 5 |

6 | 7 | ### Installation 8 | 9 | ```sh 10 | $ npm install app-decorators-element-to-function --save 11 | ``` 12 | 13 | ### Usage 14 | 15 | ### The goal of this babel-plugin is for app-decorators @component: 16 | see: https://github.com/babel/babel/issues/1548 17 | 18 | app-decorators-element-to-function.js: 19 | ```js 20 | let elementToFunction = Element => { 21 | if(typeof Element === 'function'){ 22 | return Element; 23 | } 24 | 25 | let _Element = function(){}; 26 | _Element.prototype = Element.prototype; 27 | return _Element; 28 | 29 | }; 30 | ``` 31 | 32 | ```js 33 | import ElementToFunc from 'app-decorators-element-to-function'; 34 | class Foo extends ElementToFunc(HTMLImageElement) { 35 | 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /packages/app-decorators-element-to-function/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | let elementToFunction = Element => { 8 | if(typeof Element === 'function'){ 9 | return Element; 10 | } 11 | 12 | let _Element = function(){}; 13 | _Element.prototype = Element.prototype; 14 | return _Element; 15 | }; 16 | 17 | exports.default = elementToFunction; -------------------------------------------------------------------------------- /packages/app-decorators-element-to-function/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-decorators-element-to-function", 3 | "description": "babel plugin for app-decorators to extends an HTMLElement in IE and Safari", 4 | "version": "0.8.252", 5 | "author": "Serkan Sipahi ", 6 | "main": "index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/app-decorators-element-to-function" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "prepublish": "echo 'nothing to prepublish!'", 17 | "test": "echo 'no tests!'" 18 | }, 19 | "keywords": [ 20 | "polyfill", 21 | "fix" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/app-decorators-simple-it/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-es2015-modules-commonjs"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/app-decorators-simple-it/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-decorators-simple-it", 3 | "version": "0.8.252", 4 | "description": "Simple it tester", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "prepublish": "npm run clean && ./node_modules/.bin/babel src -d lib", 8 | "clean": "rm -rf lib", 9 | "test": "echo 'no tests required'" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/app-decorators-simple-it" 14 | }, 15 | "keywords": [ 16 | "test", 17 | "it", 18 | "simple" 19 | ], 20 | "author": "Serkan Sipahi ", 21 | "license": "MIT", 22 | "dependencies": { 23 | "babel-cli": "6.26.0", 24 | "babel-plugin-transform-es2015-modules-commonjs": "6.26.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/app-decorators-simple-it/src/index.js: -------------------------------------------------------------------------------- 1 | let its = []; 2 | let only = false; 3 | 4 | let it = (description, test) => its.push([description, test, false]); 5 | it.only = (description, test) => its.push([description, test, true]); 6 | it.skip = description => console.log(`Skip: ${description}`); 7 | 8 | let testSuccLog = value => console.log(`Test Ok: ${value}`); 9 | let testFailLog = (value, e) => (`Failed in: ${value}, Message:`, e.stack, 'Actual: ', e.actual); 10 | 11 | it.run = () => { 12 | 13 | if(only) return; 14 | 15 | let _its = its.filter(item => item[2]); 16 | _its.length && (its = _its); 17 | 18 | for(let [description, test] of its){ 19 | try { 20 | test(); 21 | testSuccLog(description); 22 | } catch(e){ 23 | throw new Error(testFailLog(description, e)); 24 | } 25 | } 26 | }; 27 | 28 | export { it } -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { 4 | "modules" : "systemjs" 5 | }], 6 | "app-decorators" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Serkan Sipahi 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. 22 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | @echo "" 3 | @echo "Available Targets:" 4 | @echo "" 5 | @echo " make start" 6 | @echo " make compile" 7 | @echo " make clean" 8 | @echo " make test" 9 | @echo "" 10 | 11 | install: npm-install-packages jspm-install-packages 12 | 13 | production: prepare-compile compile bundle-app minify-core-files 14 | 15 | dev: prepare-compile gulp-compile minify-core-files 16 | 17 | compile: prepare-compile 18 | $(babel) src --out-dir lib 19 | 20 | gulp-compile: prepare-compile 21 | $(gulp) compile 22 | 23 | gulp-compile-watch: prepare-compile 24 | $(gulp) compile-watch 25 | 26 | server: 27 | node index.js 28 | 29 | bundle-app: 30 | $(jspm) bundle lib/app - app-decorators dist/lib/app.js \ 31 | --skip-source-maps \ 32 | --minify; 33 | 34 | minify-core-files: 35 | $(uglifyjs) ./node_modules/systemjs/dist/system.js \ 36 | ./node_modules/app-decorators/bootstrap.js \ 37 | ./node_modules/app-decorators/jspm.config.js \ 38 | ./node_modules/app-decorators/runtime.js \ 39 | -o dist/runtime.js \ 40 | -c -m 41 | 42 | npm-install-packages: 43 | npm install 44 | 45 | jspm-install-packages: 46 | $(jspm) install 47 | 48 | clean: 49 | make clean-dist; rm -rf node_modules jspm_packages 50 | 51 | clean-dist: 52 | rm -rf dist lib; 53 | 54 | prepare-compile: clean-dist 55 | mkdir -p dist; \ 56 | cp index.html dist; \ 57 | cd dist; \ 58 | ln -sf ../jspm_packages jspm_packages; \ 59 | ln -sf ../node_modules node_modules; \ 60 | cd ..; 61 | 62 | babel = ./node_modules/.bin/babel 63 | gulp = ./node_modules/.bin/gulp 64 | jspm = ./node_modules/.bin/jspm 65 | uglifyjs = ./node_modules/.bin/uglifyjs 66 | 67 | .PHONY: compile compile-watch live production bundle-app server prepare-compile; 68 | MAKEFLAGS = -s 69 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | # app-decorators-todomvc 3 | 4 |

5 | Dependency Status 6 | devDependency Status 7 |

8 | --- 9 |

10 | todomvc app-decorators 11 |

12 | --- 13 | 14 | #### Playground environment for todomvc 15 | ```bash 16 | git clone https://github.com/SerkanSipahi/app-decorators.git 17 | cd app-decorators/packages/app-decorators-todomvc 18 | make install 19 | ``` 20 | then 21 | ```bash 22 | make dev server 23 | ``` 24 | or 25 | ``` 26 | make production server 27 | ``` 28 | 29 | open following url `http://localhost:3000/` in your browser. -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/assets/todomvc-app-decorators.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerkanSipahi/app-decorators/15f046b1bbe56af0d45b4a1b45a6a4c5689e4763/packages/app-decorators-todomvc/assets/todomvc-app-decorators.png -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const sourcemaps = require('gulp-sourcemaps'); 3 | const babel = require('gulp-babel'); 4 | const changed = require('gulp-changed'); 5 | 6 | gulp.task('compile:src', () => { 7 | return gulp.src('src/**/*.js') 8 | .pipe(changed('./')) 9 | .pipe(sourcemaps.init()) 10 | .pipe(babel()) 11 | .pipe(sourcemaps.write('.')) 12 | .pipe(gulp.dest('dist/lib')); 13 | }); 14 | 15 | gulp.task('compile:test', () => { 16 | return gulp.src('test/**/*.js') 17 | .pipe(changed('./')) 18 | .pipe(sourcemaps.init()) 19 | .pipe(babel()) 20 | .pipe(sourcemaps.write('.')) 21 | .pipe(gulp.dest('./dist/test')); 22 | }); 23 | 24 | gulp.task('watch', function() { 25 | gulp.watch( 26 | ['src/**/*.js', 'test/**/*.js'], 27 | ['compile:src', 'compile:test'] 28 | ); 29 | }); 30 | 31 | gulp.task('compile', ['compile:src', 'compile:test']); 32 | gulp.task('compile-watch', ['compile:src', 'compile:test', 'watch']); 33 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | App-Decorators • TodoMVC 7 | 8 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/index.js: -------------------------------------------------------------------------------- 1 | let compression = require('compression'); 2 | let express = require('express'); 3 | let app = express(); 4 | app.use(compression()); 5 | 6 | let [,,type, number] = process.argv; 7 | let port = 3000; 8 | type === 'port' && number && (port = number); 9 | 10 | app.use(express.static('./dist')); 11 | app.listen(port, () => console.log(`Server: localhost: ${port}`)); 12 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/jspm.config.js: -------------------------------------------------------------------------------- 1 | SystemJS.config({ 2 | "transpiler": false, 3 | "paths": { 4 | "github": "jspm_packages/github/", 5 | "npm:": "jspm_packages/npm/", 6 | "app-decorators/": "node_modules/app-decorators/" 7 | }, 8 | "map": { 9 | "app-decorators": "app-decorators/src/index", 10 | "app-decorators/src/bootstrap": "app-decorators/src/bootstrap", 11 | "app-decorators/src/libs/customelement": "app-decorators/src/libs/customelement", 12 | "app-decorators/src/libs/random-storage": "app-decorators/src/libs/random-storage", 13 | "app-decorators/src/libs/element-to-function": "app-decorators/src/libs/element-to-function", 14 | }, 15 | "packages": { 16 | "lib": { 17 | "defaultExtension": "js" 18 | }, 19 | "node_modules": { 20 | "defaultExtension": "js" 21 | } 22 | } 23 | }); 24 | 25 | SystemJS.config({ 26 | packageConfigPaths: [ 27 | "npm:@*/*.json", 28 | "npm:*.json", 29 | "github:*/*.json" 30 | ], 31 | map: { 32 | "jquery": "npm:jquery@3.2.1" 33 | }, 34 | packages: {} 35 | }); -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-decorators-todomvc", 3 | "version": "0.8.252", 4 | "description": "Todomvc with app-decorators", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/SerkanSipahi/app-decorators-todomvc.git" 8 | }, 9 | "author": "Serkan Sipahi", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 13 | }, 14 | "scripts": { 15 | "prepublish": "make compile", 16 | "test": "echo 'no tests required'" 17 | }, 18 | "homepage": "https://github.com/SerkanSipahi/app-decorators-todomvc#readme", 19 | "keywords": [ 20 | "todomvc", 21 | "todomvc app-decorators", 22 | "example app-decorators", 23 | "es2015", 24 | "babeljs", 25 | "decorators", 26 | "component", 27 | "view", 28 | "webapp" 29 | ], 30 | "dependencies": { 31 | "app-decorators": "0.8.252", 32 | "babel-preset-app-decorators": "0.8.252", 33 | "babel-preset-es2015": "6.24.1", 34 | "compression": "1.7.2", 35 | "express": "4.16.3", 36 | "systemjs": "0.21.4", 37 | "todomvc-app-css": "2.1.2", 38 | "todomvc-common": "1.0.5" 39 | }, 40 | "devDependencies": { 41 | "babel-cli": "6.26.0", 42 | "gulp": "3.9.1", 43 | "gulp-babel": "6.1.2", 44 | "gulp-changed": "3.2.0", 45 | "gulp-concat": "2.6.1", 46 | "gulp-replace": "1.0.0", 47 | "gulp-sourcemaps": "2.6.4", 48 | "jspm": "0.17.0-beta.47", 49 | "uglify-es": "3.3.9" 50 | }, 51 | "jspm": { 52 | "dependencies": { 53 | "jquery": "npm:jquery@3.2.1" 54 | }, 55 | "devDependencies": {}, 56 | "overrides": { 57 | "npm:jquery@3.2.1": { 58 | "format": "amd" 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/src/app.js: -------------------------------------------------------------------------------- 1 | import { component, view, on, action, style } from 'app-decorators'; 2 | import { addClass, removeClass, show, hide, text, click } from './dom'; 3 | 4 | import './todo-new'; 5 | import './todo-list'; 6 | 7 | @style(` 8 | @on load { 9 | @fetch ./node_modules/todomvc-app-css/index.css; 10 | } 11 | @fetch ./node_modules/todomvc-common/base.css; 12 | `) 13 | @view(` 14 |
15 |
16 |

todos

17 | 18 |
19 |
20 | 21 | 22 |
    23 |
    24 |
    25 | 0 items left 26 | 31 | 32 |
    33 |
    34 | `) 35 | @component({ 36 | name: 'app-todomvc', 37 | }) 38 | class Todomvc { 39 | 40 | @on('count [is="todo-list"]') onListCount({ params }){ 41 | let $ = ::this.querySelector; 42 | let { count, left } = params; 43 | 44 | $('.todo-count strong')::text(left); 45 | 46 | if(count > 0){ 47 | $('footer')::show(); 48 | } else { 49 | $('footer')::hide(); 50 | } 51 | } 52 | 53 | @on('change .toggle-all') toggleAll(){ 54 | this.querySelector('ul[is="todo-list"]').toggle(); 55 | } 56 | 57 | @on('click .clear-completed') clearCompleted(){ 58 | this.querySelector('[is="todo-list"]').clear(); 59 | } 60 | 61 | @action('/filter-{{type}}') filterItems({ params, target }){ 62 | /** 63 | * set filter 64 | */ 65 | // reset filters by removing selected class 66 | this.querySelectorAll('.filters li a')::removeClass('selected'); 67 | 68 | // add selected class to target 69 | target::addClass('selected'); 70 | 71 | /** 72 | * apply filter 73 | */ 74 | this.querySelector('ul[is="todo-list"]').filter(params.type) 75 | } 76 | } 77 | 78 | export { 79 | Todomvc 80 | } -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/src/dom.js: -------------------------------------------------------------------------------- 1 | import { Eventhandler } from 'app-decorators'; 2 | import { forEach } from './utils'; 3 | 4 | let _getNodeList = (value) => { 5 | return value.forEach ? value : [ value ]; 6 | }; 7 | 8 | let hasClass = function(cls) { 9 | 10 | this.classList.contains(cls); 11 | return this; 12 | }; 13 | 14 | let addClass = function(cls) { 15 | 16 | let nodeList = _getNodeList(this); 17 | nodeList::forEach(el => el.classList.add(cls)); 18 | 19 | return this; 20 | }; 21 | 22 | let removeClass = function(cls) { 23 | 24 | let nodeList = _getNodeList(this); 25 | nodeList::forEach(el => el.classList.remove(cls)); 26 | 27 | return this; 28 | }; 29 | 30 | let toggleClass = function(cls) { 31 | 32 | let nodeList = _getNodeList(this); 33 | nodeList::forEach(el => el.classList.toggle(cls)); 34 | 35 | return this; 36 | }; 37 | 38 | let append = function(node){ 39 | 40 | this.appendChild(node); 41 | return this; 42 | }; 43 | 44 | let remove = function() { 45 | 46 | this.parentElement.removeChild(this); 47 | }; 48 | 49 | let show = function(){ 50 | 51 | let nodeList = _getNodeList(this); 52 | nodeList::forEach(el => el.style.display = 'block'); 53 | 54 | return this; 55 | }; 56 | 57 | let hide = function(){ 58 | 59 | let nodeList = _getNodeList(this); 60 | nodeList::forEach(el => el.style.display = 'none'); 61 | 62 | return this; 63 | }; 64 | 65 | let text = function(text){ 66 | 67 | let nodeList = _getNodeList(this); 68 | nodeList::forEach(el => el.textContent = text); 69 | 70 | return this; 71 | }; 72 | 73 | let attribute = function(key, value){ 74 | 75 | if(key && value){ 76 | this.setAttribute(key, value); 77 | return this; 78 | } else if(key && !value){ 79 | return this.getAttribute(key); 80 | } 81 | }; 82 | 83 | let find = function(selector){ 84 | return this.querySelector(selector); 85 | }; 86 | 87 | let findAll = function(selector){ 88 | return this.querySelectorAll(selector); 89 | }; 90 | 91 | let parent = function(){ 92 | return this.parentElement; 93 | }; 94 | 95 | let click = function(){ 96 | 97 | this.click(); 98 | return this; 99 | 100 | }; 101 | 102 | let addListener = function(eventName, callback){ 103 | 104 | let listener = Eventhandler.create({ 105 | element: this, 106 | }); 107 | 108 | listener.on(eventName, callback); 109 | 110 | return this; 111 | 112 | }; 113 | 114 | let $ = function(query) { 115 | return document.querySelectorAll(query) 116 | }; 117 | 118 | export { 119 | hasClass, 120 | addClass, 121 | removeClass, 122 | toggleClass, 123 | append, 124 | remove, 125 | show, 126 | hide, 127 | text, 128 | attribute, 129 | find, 130 | findAll, 131 | parent, 132 | click, 133 | addListener, 134 | }; -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/src/todo-item.js: -------------------------------------------------------------------------------- 1 | import { component, view, on } from 'app-decorators'; 2 | import { remove, toggleClass } from './dom'; 3 | import { trigger } from './utils'; 4 | 5 | @view(` 6 |
    7 | 8 | 9 | 10 |
    11 | 12 | `) 13 | @component({ 14 | name: 'todo-item', 15 | extends: 'li', 16 | }) 17 | class TodoItem { 18 | 19 | @on('change li input[type="checkbox"]') onCompleted(){ 20 | 21 | this::trigger('complete'); 22 | 23 | } 24 | 25 | @on('click li button.destroy') onDeleted(){ 26 | 27 | this::trigger('delete'); 28 | 29 | } 30 | 31 | complete(){ 32 | 33 | this::toggleClass('completed'); 34 | 35 | } 36 | 37 | remove() { 38 | 39 | this::remove(); 40 | 41 | } 42 | 43 | } 44 | 45 | export { 46 | TodoItem 47 | } 48 | -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/src/todo-list.js: -------------------------------------------------------------------------------- 1 | import { component, on } from 'app-decorators'; 2 | import { TodoItem } from './todo-item'; 3 | import { forEach, trigger } from './utils'; 4 | import { append, removeClass, addClass, find, click, toggleClass, findAll } from './dom'; 5 | 6 | @component({ 7 | name: 'todo-list', 8 | extends: 'ul', 9 | }) 10 | class TodoList { 11 | 12 | /** 13 | * Events 14 | */ 15 | 16 | @on('new-item') onNewItem({ params }){ 17 | 18 | let todoItem = TodoItem.create({ text: params }); 19 | this::append(todoItem); 20 | 21 | this.update(); 22 | 23 | } 24 | 25 | @on('complete [is="todo-item"]') onComplete({ target }){ 26 | 27 | target.complete(); 28 | this.update(); 29 | 30 | } 31 | 32 | @on('delete [is="todo-item"]') onDelete({ target }){ 33 | 34 | target.remove(); 35 | this.update(); 36 | 37 | } 38 | 39 | /** 40 | * Public methods 41 | */ 42 | 43 | clear(){ 44 | 45 | this.querySelectorAll('li')::forEach( 46 | element => element.remove() 47 | ); 48 | 49 | this._triggerCounts({ 50 | count: 0, left: 0, completed: 0 51 | }); 52 | 53 | } 54 | 55 | update(){ 56 | 57 | let count = this._getAllCount(); 58 | let left = this._getLeftCount(); 59 | let completed = this._getCompleteCount(); 60 | 61 | this._triggerCounts({ 62 | count, left, completed 63 | }); 64 | } 65 | 66 | count(type){ 67 | 68 | switch(type) { 69 | case 'all': 70 | return this._getAllCount(); 71 | break; 72 | case 'left': 73 | return this._getLeftCount(); 74 | break; 75 | case 'complete': 76 | return this._getCompleteCount(); 77 | break; 78 | } 79 | 80 | } 81 | 82 | filter(type){ 83 | 84 | let $ = ::this.querySelectorAll; 85 | 86 | switch(type) { 87 | case 'all': 88 | this._applyFilterAll($); 89 | break; 90 | case 'active': 91 | this._applyFilterActive($); 92 | break; 93 | case 'completed': 94 | this._applyFilterCompleted($); 95 | break; 96 | } 97 | 98 | } 99 | 100 | toggle(){ 101 | 102 | let maxCount = this.count('all'); 103 | let completeCount = this.count('complete'); 104 | 105 | if(completeCount > 0 && completeCount < maxCount){ 106 | this::findAll('li:not(.completed)')::forEach( 107 | el => el::find('input')::click()::addClass('completed') 108 | ); 109 | } else { 110 | this::findAll('li')::forEach( 111 | el => el::find('input')::click()::toggleClass('completed') 112 | ); 113 | } 114 | 115 | } 116 | 117 | /** 118 | * Private methods 119 | */ 120 | 121 | _triggerCounts(params){ 122 | 123 | this::trigger('count', params); 124 | 125 | } 126 | 127 | _applyFilterAll($){ 128 | 129 | $('ul[is="todo-list"] li')::removeClass('hidden'); 130 | } 131 | 132 | _applyFilterActive($){ 133 | 134 | $('ul[is="todo-list"] li')::removeClass('hidden'); 135 | $('ul[is="todo-list"] li.completed')::addClass('hidden'); 136 | } 137 | 138 | _applyFilterCompleted($){ 139 | 140 | $('ul[is="todo-list"] li')::removeClass('hidden'); 141 | $('ul[is="todo-list"] li:not(.completed)')::addClass('hidden'); 142 | } 143 | 144 | _getAllCount(){ 145 | 146 | return this.querySelectorAll('li').length; 147 | 148 | } 149 | 150 | _getLeftCount(){ 151 | 152 | return this.querySelectorAll('li:not(.completed)').length; 153 | 154 | } 155 | 156 | _getCompleteCount(){ 157 | 158 | return this.querySelectorAll('li.completed').length; 159 | 160 | } 161 | 162 | } 163 | 164 | export { 165 | TodoList 166 | } -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/src/todo-new.js: -------------------------------------------------------------------------------- 1 | import { component, on } from 'app-decorators'; 2 | import { trigger } from './utils'; 3 | import { attribute } from './dom'; 4 | 5 | @component({ 6 | name: 'todo-new', 7 | extends: 'input', 8 | }) 9 | class TodoNew { 10 | 11 | @on('keypress') onKeypress({ keyCode }){ 12 | 13 | if (keyCode !== 13 || this.value === ''){ 14 | return; 15 | } 16 | 17 | let selector = this::attribute('target'); 18 | let scope = document.querySelector(selector); 19 | scope::trigger('new-item', this.value); 20 | 21 | this.value = ''; 22 | 23 | } 24 | 25 | } 26 | 27 | export { 28 | TodoNew 29 | } -------------------------------------------------------------------------------- /packages/app-decorators-todomvc/src/utils.js: -------------------------------------------------------------------------------- 1 | let trigger = function(event, params) { 2 | 3 | let customEvent = new CustomEvent(event, { bubbles: true }); 4 | customEvent.params = params; 5 | this.dispatchEvent(customEvent); 6 | }; 7 | 8 | let { forEach } = Array.prototype; 9 | 10 | export { 11 | trigger, 12 | forEach, 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /packages/app-decorators/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { 4 | "modules" : "systemjs" 5 | }], 6 | "app-decorators" 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/app-decorators/bootstrap.js: -------------------------------------------------------------------------------- 1 | window.bootstrap = function(app) { 2 | 3 | let bootstrap = Promise.all([ 4 | System.import('app-decorators/src/bootstrap'), 5 | System.import(app), 6 | ]).then(function() { 7 | console.log('App: running!'); 8 | }).catch(function() { 9 | console.warn('App: something gone wrong!', Error().stack); 10 | }); 11 | 12 | return bootstrap; 13 | }; -------------------------------------------------------------------------------- /packages/app-decorators/component.tpl: -------------------------------------------------------------------------------- 1 | import { action, component, on, view, style } from "app-decorators"; 2 | 3 | @style(` 4 | @on load { 5 | @fetch foo.css; 6 | } 7 | @on click .bar { 8 | @fetch bar.css; 9 | } 10 | @action /some/path.html { 11 | @fetch baz.css; 12 | } 13 | .critical-path-css { 14 | width: 100px; 15 | height: 100px; 16 | } 17 | `) 18 | @view(` 19 |
    20 |
    On load Foo
    21 |
    Click Bar
    22 | Click Link A 23 | Click Link B 24 | Click Link C 25 |
    26 | `) 27 | @component() 28 | class {{name}} { 29 | 30 | created(vars) { 31 | console.log('created', this, vars) 32 | } 33 | 34 | attached() { 35 | console.log('attached', this); 36 | } 37 | 38 | @on('click .bar') onClickFoo(ev) { 39 | console.log('clicked foo', ev); 40 | } 41 | 42 | @action('/some/path.html') actionA({params, search, hash}) { 43 | console.log(event, params, search, hash); 44 | } 45 | 46 | @action('?a={{value}}') actionB({params, search, hash}) { 47 | console.log(params, search); 48 | } 49 | 50 | @action('#hey={{value}}') actionC({params, hash}) { 51 | console.log(params, hash); 52 | } 53 | } 54 | 55 | export { 56 | {{name}} 57 | } -------------------------------------------------------------------------------- /packages/app-decorators/html.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com-{{name}} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 33 | 34 | -------------------------------------------------------------------------------- /packages/app-decorators/jspm.config.js: -------------------------------------------------------------------------------- 1 | SystemJS.config({ 2 | transpiler: false, 3 | paths: { 4 | "github": "jspm_packages/github/", 5 | "npm:": "jspm_packages/npm/", 6 | "app-decorators/": "node_modules/app-decorators/" 7 | }, 8 | map: { 9 | "app-decorators": "app-decorators/src/index", 10 | "app-decorators/src/bootstrap": "app-decorators/src/bootstrap", 11 | "app-decorators/src/libs/customelement": "app-decorators/src/libs/customelement", 12 | "app-decorators/src/libs/random-storage": "app-decorators/src/libs/random-storage", 13 | "app-decorators/src/libs/element-to-function": "app-decorators/src/libs/element-to-function" 14 | }, 15 | packages: { 16 | "lib": { 17 | "defaultExtension": "js" 18 | }, 19 | "node_modules": { 20 | "defaultExtension": "js" 21 | } 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /packages/app-decorators/jspm.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "transpiler": false, 3 | "packageConfigPaths": [ 4 | "npm:@*/*.json", 5 | "npm:*.json", 6 | "github:*/*.json" 7 | ], 8 | "paths": { 9 | "github": "jspm_packages/github/", 10 | "npm:": "jspm_packages/npm/", 11 | "app-decorators/": "node_modules/app-decorators/" 12 | }, 13 | "map": { 14 | "app-decorators": "app-decorators/src/index", 15 | "app-decorators/src/bootstrap": "app-decorators/src/bootstrap", 16 | "app-decorators/src/libs/customelement": "app-decorators/src/libs/customelement", 17 | "app-decorators/src/libs/random-storage": "app-decorators/src/libs/random-storage", 18 | "app-decorators/src/libs/element-to-function": "app-decorators/src/libs/element-to-function" 19 | }, 20 | "packages": { 21 | "lib": { 22 | "defaultExtension": "js" 23 | }, 24 | "node_modules": { 25 | "defaultExtension": "js" 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /packages/app-decorators/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-decorators", 3 | "version": "0.8.252", 4 | "description": "Collection of useful ES7 Decorators, writtin in ES6, that can be used for building webapps", 5 | "author": "Serkan Sipahi ", 6 | "main": "src/index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "postinstall": "npm install named-js-regexp@1.3.3 core-js@2.5.7 webcomponents.js@0.7.24 handlebars@4.0.11 extend@3.0.1 dom4@2.0.1", 17 | "test": "echo 'no tests required'" 18 | }, 19 | "keywords": [ 20 | "es6", 21 | "es7", 22 | "es2015", 23 | "es2016", 24 | "babel", 25 | "decorators", 26 | "component", 27 | "model", 28 | "view", 29 | "controller", 30 | "eventlistener", 31 | "router", 32 | "convention", 33 | "webapp", 34 | "mixin" 35 | ], 36 | "dependencies": { 37 | "babel-preset-app-decorators": "0.8.252", 38 | "babel-preset-es2015": "6.24.1", 39 | "core-js": "2.5.7", 40 | "dom4": "2.0.1", 41 | "extend": "3.0.1", 42 | "handlebars": "4.0.11", 43 | "named-js-regexp": "1.3.3", 44 | "webcomponents.js": "0.7.24" 45 | }, 46 | "devDependencies": { 47 | "babel-preset-env": "1.7.0" 48 | }, 49 | "jspm": { 50 | "name": "app-decorators", 51 | "dependencies": { 52 | "babel-polyfill": "npm:babel-polyfill@6.26.0", 53 | "handlebars": "npm:handlebars@4.0.10", 54 | "jquery": "npm:jquery@2.2.0", 55 | "should": "npm:should@11.2.1", 56 | "sinon": "npm:sinon@3.3.0", 57 | "text-encoding": "npm:text-encoding@0.6.4", 58 | "underscore": "npm:underscore@1.8.3" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/app-decorators/server.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let app = express(); 3 | 4 | let [,,type, number] = process.argv; 5 | let port = 3000; 6 | type === 'port' && number && (port = number); 7 | 8 | app.use(express.static('./')); 9 | app.listen(port, () => console.log(`Server: localhost: ${port}`)); 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-action-bundle/README.md: -------------------------------------------------------------------------------- 1 | see: https://github.com/SerkanSipahi/app-decorators/issues/48 -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component-register/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "transform-function-bind", 7 | "syntax-function-bind" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component-register/CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerkanSipahi/app-decorators/15f046b1bbe56af0d45b4a1b45a6a4c5689e4763/packages/babel-plugin-app-decorators-component-register/CHANGELOG.md -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component-register/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Serkan Sipahi 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component-register/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | @echo "" 3 | @echo "Available Targets:" 4 | @echo "" 5 | @echo " make install" 6 | @echo " make test" 7 | @echo " make publish (npm)" 8 | @echo " make clean (remove node_modules and libs/*)" 9 | @echo "" 10 | 11 | install: clean node_modules 12 | 13 | clean: 14 | rm -rf node_modules; rm -rf lib/* 15 | 16 | publish: compile npm-publish 17 | 18 | node_modules: 19 | npm install 20 | 21 | test: 22 | $(babel-node) $(mocha) -- test 23 | 24 | compile: 25 | $(babel) src -d lib; 26 | 27 | npm-publish: 28 | npm publish 29 | 30 | babel = ./node_modules/.bin/babel 31 | babel-node = ./node_modules/.bin/babel-node 32 | mocha = ./node_modules/.bin/_mocha 33 | 34 | .PHONY: install node_modules clean publish compile npm-publish test; 35 | MAKEFLAGS = -s 36 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component-register/README.md: -------------------------------------------------------------------------------- 1 | ## babel-plugin-app-decorators-component-register 2 | Babel Plugin for auto generating code 3 | 4 |

    5 | Dependency Status 6 | devDependency Status 7 |

    8 | 9 | ### Installation 10 | 11 | ```sh 12 | $ npm install babel-plugin-app-decorators-component-register --save 13 | ``` 14 | 15 | ### Usage 16 | 17 | #### Via `.babelrc` (Recommended) 18 | 19 | **.babelrc** 20 | 21 | ```json 22 | { 23 | "plugins": ["app-decorators-component-register"] 24 | } 25 | ``` 26 | 27 | **.babelrc options** 28 | ```js 29 | "plugins": [ 30 | ["app-decorators-component-register", { 31 | "imports": [ 32 | { "IMPORT_NAME": "Register", "SOURCE": "app-decorators-helper/register-customelement" }, 33 | { "IMPORT_NAME": "storage", "SOURCE": "app-decorators-helper/random-storage" } 34 | ] 35 | }] 36 | ] 37 | ``` 38 | 39 | #### Via CLI 40 | 41 | ```sh 42 | $ babel --plugins app-decorators-component-register script.js 43 | ``` 44 | 45 | #### Via Node API 46 | 47 | ```js 48 | require('babel').transform('code', { 49 | plugins: ['app-decorators-component-register'] 50 | }); 51 | ``` 52 | 53 | ### The goal of this babel-plugin is for app-decorators @component: 54 | 55 | #### Example 1 56 | code: 57 | ```js 58 | @component() 59 | class Foo { 60 | 61 | } 62 | ``` 63 | transformed: 64 | ```js 65 | import * as _register from 'app-decorators-helper/register-document'; 66 | import * as _storage from 'app-decorators-helper/registry-storage'; 67 | 68 | @component() 69 | class Foo { 70 | 71 | } 72 | 73 | _register.Register.customElement(Foo, _storage.storage); 74 | ``` 75 | 76 | 77 | #### Tests 78 | ```bash 79 | git clone https://github.com/SerkanSipahi/app-decorators.git 80 | cd app-decorators/packages/babel-plugin-app-decorators-component-register 81 | make install 82 | make test 83 | ``` 84 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component-register/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-app-decorators-component-register", 3 | "description": "Babel plugin for app-decorators @component", 4 | "version": "0.8.252", 5 | "author": "Serkan Sipahi ", 6 | "main": "lib/index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/babel-plugin-app-decorators-component-register" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "prepublish": "make compile", 17 | "test": "make test" 18 | }, 19 | "keywords": [ 20 | "babel", 21 | "babel-plugin", 22 | "es7", 23 | "decorators", 24 | "component" 25 | ], 26 | "devDependencies": { 27 | "babel-cli": "6.26.0", 28 | "babel-core": "6.26.3", 29 | "babel-plugin-syntax-function-bind": "6.13.0", 30 | "babel-preset-es2015": "6.24.1", 31 | "babel-template": "6.26.0", 32 | "babel-types": "6.26.0", 33 | "chai": "4.1.2", 34 | "mocha": "5.2.0" 35 | }, 36 | "dependencies": { 37 | "babel-plugin-syntax-decorators": "6.13.0", 38 | "babel-plugin-transform-function-bind": "6.22.0", 39 | "babel-runtime": "6.26.0", 40 | "babylon": "6.18.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "transform-runtime", 7 | "transform-function-bind" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerkanSipahi/app-decorators/15f046b1bbe56af0d45b4a1b45a6a4c5689e4763/packages/babel-plugin-app-decorators-component/CHANGELOG.md -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Serkan Sipahi 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | @echo "" 3 | @echo "Available Targets:" 4 | @echo "" 5 | @echo " make install" 6 | @echo " make test" 7 | @echo " make publish (npm)" 8 | @echo " make clean (remove node_modules and libs/*)" 9 | @echo "" 10 | 11 | install: clean node_modules 12 | 13 | clean: 14 | rm -rf node_modules; rm -rf lib/* 15 | 16 | publish: compile npm-publish 17 | 18 | node_modules: 19 | npm install 20 | 21 | test: 22 | $(babel-node) $(mocha) -- test 23 | 24 | compile: 25 | $(babel) src -d lib; 26 | 27 | npm-publish: 28 | npm publish 29 | 30 | babel = ./node_modules/.bin/babel 31 | babel-node = ./node_modules/.bin/babel-node 32 | mocha = ./node_modules/.bin/_mocha 33 | 34 | .PHONY: install node_modules clean publish compile npm-publish test; 35 | MAKEFLAGS = -s 36 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/README.md: -------------------------------------------------------------------------------- 1 | ## babel-plugin-app-decorators-component 2 | Babel Plugin for extend HTMLElement by options for Babeljs v6.x 3 | 4 |

    5 | Dependency Status 6 | devDependency Status 7 |

    8 | 9 | ### Installation 10 | 11 | ```sh 12 | $ npm install babel-plugin-app-decorators-component --save 13 | ``` 14 | 15 | ### Usage 16 | 17 | #### Via `.babelrc` (Recommended) 18 | 19 | **.babelrc** 20 | 21 | ```json 22 | { 23 | "plugins": ["app-decorators-component"] 24 | } 25 | ``` 26 | 27 | #### Note: Order of Plugins Matters! 28 | If you including your plugin `app-decorators-component`, make sure that `app-decorators-component` 29 | comes *before* all plugins or if you using `transform-decorators-legacy` before that. 30 | 31 | ```js 32 | // WRONG 33 | "plugins": [ 34 | "plugin-1", 35 | "plugin-2", 36 | "plugin-3", 37 | "app-decorators-component" 38 | ] 39 | 40 | // RIGHT 41 | "plugins": [ 42 | "app-decorators-component", 43 | "plugin-1", 44 | "plugin-2", 45 | "plugin-3" 46 | ] 47 | ``` 48 | See also notes: https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy 49 | 50 | #### Via CLI 51 | 52 | ```sh 53 | $ babel --plugins app-decorators-component script.js 54 | ``` 55 | 56 | #### Via Node API 57 | 58 | ```js 59 | require('babel').transform('code', { 60 | plugins: ['app-decorators-component'] 61 | }); 62 | ``` 63 | 64 | ### The goal of this babel-plugin for app-decorators @component: 65 | 66 | #### Example 1 67 | code: 68 | ```js 69 | @component() 70 | class Helloworld { 71 | 72 | } 73 | ``` 74 | transformed: 75 | ```js 76 | import _elementToFunc from 'app-decorators-element-to-function'; 77 | @component() 78 | class Helloworld extends _elementToFunc(HTMLElement) { 79 | 80 | } 81 | ``` 82 | 83 | #### Example 2 84 | code: 85 | ```js 86 | @component({ 87 | extends: 'img' 88 | }) 89 | class Helloworld { 90 | 91 | } 92 | ``` 93 | 94 | transformed: 95 | ```js 96 | import _elementToFunc from 'app-decorators-element-to-function'; 97 | @component({ 98 | extends: 'img' 99 | }) 100 | class Helloworld extends _elementToFunc(HTMLImageElement) { 101 | static get extends() { 102 | return 'img'; 103 | } 104 | } 105 | ``` 106 | 107 | #### Not possible at the moment: 108 | ```js 109 | 110 | class Bar {} 111 | 112 | @component() 113 | class Foo extends Bar { 114 | 115 | } 116 | ``` 117 | #### Use dependency-Injection instead: 118 | * https://en.wikipedia.org/wiki/Dependency_injection 119 | 120 | #### Tests 121 | ```bash 122 | git clone https://github.com/SerkanSipahi/app-decorators.git 123 | cd app-decorators/packages/babel-plugin-app-decorators-component 124 | make install 125 | make test 126 | ``` 127 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-app-decorators-component", 3 | "description": "Babel plugin for app-decorators @component", 4 | "version": "0.8.252", 5 | "author": "Serkan Sipahi ", 6 | "main": "lib/index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/babel-plugin-app-decorators-component" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "prepublish": "make compile", 17 | "test": "make test" 18 | }, 19 | "keywords": [ 20 | "babel", 21 | "babel-plugin", 22 | "es7", 23 | "decorators", 24 | "component" 25 | ], 26 | "devDependencies": { 27 | "babel-cli": "6.26.0", 28 | "babel-core": "6.26.3", 29 | "babel-plugin-transform-runtime": "6.23.0", 30 | "babel-preset-es2015": "6.24.1", 31 | "chai": "4.1.2", 32 | "mocha": "5.2.0" 33 | }, 34 | "dependencies": { 35 | "babel-plugin-syntax-decorators": "6.13.0", 36 | "babel-plugin-transform-function-bind": "6.22.0", 37 | "babel-runtime": "6.26.0", 38 | "babel-traverse": "6.26.0", 39 | "babylon": "6.18.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/src/elements.js: -------------------------------------------------------------------------------- 1 | let elements = { 2 | 3 | 'a': 'HTMLAnchorElement', 4 | 'area': 'HTMLAreaElement', 5 | 'br': 'HTMLBRElement', 6 | 'base': 'HTMLBaseElement', 7 | 'body': 'HTMLBodyElement', 8 | 'button': 'HTMLButtonElement', 9 | 'canvas': 'HTMLCanvasElement', 10 | 'content': 'HTMLContentElement', 11 | 'dl': 'HTMLDListElement', 12 | 'data': 'HTMLDataElement', 13 | 'datalist': 'HTMLDataListElement', 14 | 'div': 'HTMLDivElement', 15 | 'document': 'HTMLDocument', 16 | 'fieldset': 'HTMLFieldSetElement', 17 | 'form': 'HTMLFormElement', 18 | 'framset': 'HTMLFrameSetElement', 19 | 'hr': 'HTMLHRElement', 20 | 'head': 'HTMLHeadElement', 21 | 'h': 'HTMLHeadingElement', 22 | 'html': 'HTMLHtmlElement', 23 | 'iframe': 'HTMLIFrameElement', 24 | 'img': 'HTMLImageElement', 25 | 'input': 'HTMLInputElement', 26 | 'keygen': 'HTMLKeygenElement', 27 | 'li': 'HTMLLIElement', 28 | 'label': 'HTMLLabelElement', 29 | 'legend': 'HTMLLegendElement', 30 | 'link': 'HTMLLinkElement', 31 | 'map': 'HTMLMapElement', 32 | 'media': 'HTMLMediaElement', 33 | 'meta': 'HTMLMetaElement', 34 | 'mod': 'HTMLModElement', 35 | 'ol': 'HTMLOListElement', 36 | 'object': 'HTMLObjectElement', 37 | 'optgroup': 'HTMLOptGroupElement', 38 | 'option': 'HTMLOptionElement', 39 | 'output': 'HTMLOutputElement', 40 | 'p': 'HTMLParagraphElement', 41 | 'param': 'HTMLParamElement', 42 | 'picture': 'HTMLPictureElement', 43 | 'pre': 'HTMLPreElement', 44 | 'quote': 'HTMLQuoteElement', 45 | 'script': 'HTMLScriptElement', 46 | 'select': 'HTMLSelectElement', 47 | 'shadow': 'HTMLShadowElement', 48 | 'span': 'HTMLSpanElement', 49 | 'style': 'HTMLStyleElement', 50 | 'caption': 'HTMLTableCaptionElement', 51 | 'td': 'HTMLTableCellElement', 52 | 'col': 'HTMLTableColElement', 53 | 'table': 'HTMLTableElement', 54 | 'th': 'HTMLTableCellElement', 55 | 'tr': 'HTMLTableRowElement', 56 | 'template': 'HTMLTemplateElement', 57 | 'textarea': 'HTMLTextAreaElement', 58 | 'title': 'HTMLTitleElement', 59 | 'ul': 'HTMLUListElement', 60 | 'unknown': 'HTMLUnknownElement', 61 | 62 | 'article': 'HTMLElement', 63 | 'aside': 'HTMLElement', 64 | 'bdi': 'HTMLElement', 65 | 'details': 'HTMLDetailsElement', 66 | 'dialog': 'HTMLDialogElement', 67 | 'figcaption': 'HTMLElement', 68 | 'figure': 'HTMLElement', 69 | 'footer': 'HTMLElement', 70 | 'header': 'HTMLElement', 71 | 'main': 'HTMLElement', 72 | 'mark': 'HTMLElement', 73 | 'menuitem': 'HTMLUnknownElement', 74 | 'meter': 'HTMLMeterElement', 75 | 'nav': 'HTMLElement', 76 | 'progress': 'HTMLProgressElement', 77 | 'rp': 'HTMLElement', 78 | 'rt': 'HTMLElement', 79 | 'ruby': 'HTMLElement', 80 | 'section': 'HTMLElement', 81 | 'summary': 'HTMLElement', 82 | 'time': 'HTMLTimeElement', 83 | 'wbr': 'HTMLElement', 84 | 85 | 'svg': 'SVGElement', 86 | 'audio': 'HTMLAudioElement', 87 | 'embed': 'HTMLEmbedElement', 88 | 'source': 'HTMLSourceElement', 89 | 'track': 'HTMLTrackElement', 90 | 'video': 'HTMLVideoElement', 91 | 92 | }; 93 | 94 | export { 95 | elements 96 | }; 97 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/src/index.js: -------------------------------------------------------------------------------- 1 | import { elements } from './elements'; 2 | 3 | /** 4 | * getDecorator 5 | * @param decoratorName 6 | * @returns {object|null} 7 | */ 8 | let getDecorator = function(decoratorName) { 9 | 10 | let decorators = this.decorators || []; 11 | 12 | for(let decorator of decorators){ 13 | let { name } = decorator.expression.callee; 14 | if(decoratorName === name){ 15 | return decorator; 16 | } 17 | } 18 | 19 | return null; 20 | }; 21 | 22 | /** 23 | * getElementClassByName 24 | * @param name {string} 25 | * @param elements {object} 26 | * @returns {*|string} 27 | */ 28 | let getElementClassByName = function(name, elements) { 29 | 30 | let element = elements[name] || 'HTMLElement'; 31 | return element; 32 | }; 33 | 34 | /** 35 | * getArguments 36 | * @param t {object} 37 | * @returns {{extends: string}} 38 | */ 39 | let getArguments = function(t){ 40 | 41 | // set default 42 | let args = { 43 | extends: 'div', 44 | }; 45 | let [ firstArgument ] = this.expression.arguments; 46 | 47 | if(!t.isObjectExpression(firstArgument)){ 48 | return args; 49 | } 50 | 51 | let { properties } = firstArgument; 52 | for(let property of properties){ 53 | args[property.key.name] = property.value.value 54 | } 55 | 56 | return args; 57 | }; 58 | 59 | /** 60 | * addSuperClass 61 | * @param element {string} 62 | * @param t {object} 63 | */ 64 | let addSuperClass = function(element, t) { 65 | 66 | let identifier = t.identifier(element); 67 | this.superClass = identifier; 68 | }; 69 | 70 | /** 71 | * add static string property 72 | * @param prop {string} 73 | * @param value {string} 74 | * @param t {object} 75 | */ 76 | let addStaticStringProperty = function(prop, value, t) { 77 | 78 | let a = t.classProperty( 79 | t.identifier(prop), 80 | t.stringLiteral(value) 81 | ); 82 | a.static = true; 83 | this.body.body.push(a); 84 | }; 85 | 86 | /** 87 | * addStaticGetterProperty 88 | * @param type {string} 89 | * @param superClass {string} 90 | * @param t {object} 91 | */ 92 | let addStaticGetterProperty = function(type, superClass, t) { 93 | 94 | let classMethodNode = t.classMethod( 95 | 'get', 96 | t.identifier(type), 97 | [], 98 | t.blockStatement([ 99 | t.returnStatement(t.stringLiteral(superClass)) 100 | ]), 101 | false, 102 | true 103 | ); 104 | 105 | this.body.body.push(classMethodNode); 106 | }; 107 | 108 | /** 109 | * getProgram 110 | * @param node 111 | * @returns {*} 112 | */ 113 | let getProgram = node => { 114 | while(node.parent){ 115 | node = node.findParent(node => node); 116 | if(node.key === "program"){ 117 | return node; 118 | } 119 | } 120 | }; 121 | 122 | /** 123 | * plugin 124 | * @astexplorer: 125 | * - https://astexplorer.net/#/gist/844e84f3b94e1b4b3a4e1e30b9abc8d2/934cc82a41d787ba43ec9413a570be7639b305b4 126 | * @param t {types} 127 | * @returns {{visitor: {ClassDeclaration: (function(*=))}}} 128 | */ 129 | let plugin = ({types: t}) => { 130 | 131 | return { 132 | pre(){ 133 | this.cache = new Map(); 134 | }, 135 | post(){ 136 | this.cache.clear(); 137 | }, 138 | visitor: { 139 | Program(path, { opts } = state) { 140 | 141 | // shim elementToFunction 142 | if(!opts.elementToFunc){ 143 | return; 144 | } 145 | 146 | let programBody = path.node.body; 147 | let importSrcPath = "app-decorators/src/libs/element-to-function"; 148 | 149 | // return when import for elementToFunc already exists 150 | for(let node of programBody) { 151 | if(node.type !== "ImportDeclaration") continue; 152 | if(node.source.value === importSrcPath) return; 153 | } 154 | 155 | let name = this.cache.get("elementToFunc"); 156 | if(!name){ 157 | name = path.scope.generateUidIdentifier("elementToFunc").name; 158 | this.cache.set("elementToFunc", name); 159 | } 160 | 161 | // create import statement for elementToFunc 162 | let importDeclaration = t.importDeclaration( 163 | [t.importDefaultSpecifier(t.identifier(name))], 164 | t.stringLiteral(importSrcPath) 165 | ); 166 | 167 | // add import statement for elementToFunc 168 | programBody.unshift(importDeclaration); 169 | 170 | }, 171 | /** 172 | * ClassDeclaration 173 | * @param node {object} 174 | * @param opts {object} 175 | */ 176 | ClassDeclaration(path, { opts } = state) { 177 | 178 | let options = opts || {}; 179 | let { node } = path; 180 | 181 | let component = node::getDecorator('component'); 182 | if(!component){ 183 | 184 | /** 185 | * If no @component but HTML... as superClass 186 | * and no extends() then add addStaticGetterProperty 187 | */ 188 | return; 189 | } 190 | 191 | let className = node.id.name; 192 | node::addStaticStringProperty('$$componentName', className, t); 193 | 194 | let superClass = component::getArguments(t).extends; 195 | let element = getElementClassByName(superClass, elements); 196 | node::addSuperClass(element, t); 197 | 198 | if(!/div/.test(superClass)){ 199 | node::addStaticGetterProperty('extends', superClass, t); 200 | } 201 | 202 | // shim elementToFunction 203 | if(!options.elementToFunc){ 204 | return; 205 | } 206 | 207 | /** 208 | * Begin for elementToFunc shim 209 | */ 210 | 211 | // replace extends identifier with "elementToFunc" 212 | let name = this.cache.get("elementToFunc"); 213 | 214 | let callExpressionNode = t.callExpression(t.identifier(name), [t.identifier(element)]); 215 | path.node.superClass = callExpressionNode; 216 | 217 | }, 218 | }, 219 | }; 220 | }; 221 | 222 | export default plugin; 223 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-component/test/index.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { transform } from 'babel-core'; 3 | import appDecoratorComponent from'../src/index'; 4 | import syntaxDecorator from 'babel-plugin-syntax-decorators'; 5 | 6 | function trim(str) { 7 | return str.replace(/^\s+|\s+/gm, ''); 8 | } 9 | 10 | function transformCode(code, options = {}){ 11 | 12 | // transform code 13 | let generated = transform(code, { 14 | plugins: [ 15 | [appDecoratorComponent, options], 16 | syntaxDecorator 17 | ] 18 | 19 | }); 20 | 21 | return generated; 22 | } 23 | 24 | describe('@component', () => { 25 | 26 | it('should add class extends HTMLElement when no options passed', () => { 27 | 28 | let actual =` 29 | @component() 30 | class Foo { 31 | }`; 32 | 33 | let expected = ` 34 | @component() 35 | class Foo extends HTMLDivElement { 36 | static $$componentName = "Foo"; 37 | }`; 38 | 39 | let generated = transformCode(actual); 40 | 41 | assert.equal(trim(generated.code), trim(expected)); 42 | 43 | }); 44 | 45 | it('should add "class extends HTML{type}" when options passed', () => { 46 | 47 | let actual =` 48 | @component({ 49 | extends: 'img' 50 | }) 51 | class Foo { 52 | }`; 53 | 54 | let expected =` 55 | @component({ 56 | extends: 'img' 57 | }) 58 | class Foo extends HTMLImageElement { 59 | static $$componentName = 'Foo'; 60 | static get extends() { 61 | return 'img'; 62 | }}`; 63 | 64 | let generated = transformCode(actual); 65 | assert.equal(trim(generated.code), trim(expected)); 66 | 67 | }); 68 | 69 | it('should add "class extends HTML{type}" by mixed classes', () => { 70 | 71 | let actual =` 72 | class Foo {} 73 | 74 | @component({ 75 | extends: 'progress' 76 | }) 77 | class Bar { 78 | } 79 | 80 | class Baz {}`; 81 | 82 | let expected =` 83 | class Foo {} 84 | 85 | @component({ 86 | extends: 'progress' 87 | }) 88 | class Bar extends HTMLProgressElement { 89 | static $$componentName = 'Bar'; 90 | static get extends() { 91 | return 'progress'; 92 | }} 93 | 94 | class Baz {}`; 95 | 96 | let generated = transformCode(actual); 97 | assert.equal(trim(generated.code), trim(expected)); 98 | 99 | }); 100 | 101 | it('should wrap element with _elementToFunc when its set by option', () => { 102 | 103 | let actual =` 104 | import x from 'x'; 105 | class Foo {} 106 | @component({ 107 | extends: 'progress' 108 | }) 109 | class Bar { 110 | } 111 | class Kick {} 112 | @component() 113 | class Baz {}`; 114 | 115 | let expected =` 116 | import _elementToFunc from 'app-decorators/src/libs/element-to-function'; 117 | import x from 'x'; 118 | class Foo {} 119 | @component({ 120 | extends: 'progress' 121 | }) 122 | class Bar extends _elementToFunc(HTMLProgressElement) { 123 | static $$componentName = 'Bar'; 124 | static get extends() { 125 | return 'progress'; 126 | } 127 | 128 | } 129 | class Kick {} 130 | @component() 131 | class Baz extends _elementToFunc(HTMLDivElement) { 132 | static $$componentName = 'Baz'; 133 | }`; 134 | 135 | let generated = transformCode(actual, { elementToFunc: true }); 136 | assert.equal(trim(generated.code), trim(expected)); 137 | 138 | }); 139 | 140 | it.skip('should add static get extends() if HTML{type}Element already defined', () => { 141 | 142 | let actual =` 143 | class Bar extends HTMLImageElement { 144 | }`; 145 | 146 | let expected =` 147 | class Bar extends HTMLImageElement { 148 | static get extends() { 149 | return 'img'; 150 | }}`; 151 | 152 | let generated = transformCode(actual); 153 | assert.equal(trim(generated.code), trim(expected)); 154 | 155 | }); 156 | 157 | it.skip('should add HTML{type}Element if decorators already exists', () => { 158 | 159 | let actual =` 160 | @component({ 161 | extends: 'img' 162 | }) 163 | class Bar extends HTMLImageElement { 164 | }`; 165 | 166 | let expected =` 167 | @component({ 168 | extends: 'img' 169 | }) 170 | class Bar extends HTMLImageElement { 171 | static get extends() { 172 | return 'img'; 173 | }}`; 174 | 175 | let generated = transformCode(actual); 176 | assert.equal(trim(generated.code), trim(expected)); 177 | 178 | }); 179 | 180 | it.skip('should resolve passed "x" identifier', () => { 181 | 182 | let actual =` 183 | let x = 'img'; 184 | @component({ 185 | extends: x 186 | }) 187 | class Bar { 188 | }`; 189 | 190 | let expected =` 191 | let x = 'img'; 192 | @component({ 193 | extends: x 194 | }) 195 | class Bar extends HTMLImageElement { 196 | static get extends() { 197 | return 'img'; 198 | }}`; 199 | 200 | let generated = transformCode(actual); 201 | assert.equal(trim(generated.code), trim(expected)); 202 | 203 | }); 204 | 205 | it.skip('should resolve passed "x" identifier', () => { 206 | 207 | let actual =` 208 | let x = { 209 | extends: 'form' 210 | }; 211 | @component(x) 212 | class Bar { 213 | }`; 214 | 215 | let expected =` 216 | let x = { 217 | extends: 'form' 218 | }; 219 | @component(x) 220 | class Bar extends HTMLFormElement { 221 | static get extends() { 222 | return 'form'; 223 | }}`; 224 | 225 | let generated = transformCode(actual); 226 | assert.equal(trim(generated.code), trim(expected)); 227 | 228 | }); 229 | 230 | it.skip('should resolve class', () => { 231 | 232 | let actual =` 233 | class Bar {} 234 | 235 | @component() 236 | class Foo extends Bar { 237 | 238 | }`; 239 | 240 | let expected =` 241 | class Bar extends HTMLElement {} 242 | 243 | @component() 244 | class Foo extends Bar { 245 | 246 | }`; 247 | 248 | let generated = transformCode(actual); 249 | assert.equal(trim(generated.code), trim(expected)); 250 | 251 | }); 252 | 253 | }); 254 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-element-to-function/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-es2015-modules-commonjs" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-element-to-function/README.md: -------------------------------------------------------------------------------- 1 | This repository is deprecated. Its merged into: [app-decorators/packages/babel-plugin-app-decorators-component](https://github.com/SerkanSipahi/app-decorators/tree/master/packages/babel-plugin-app-decorators-component) 2 | --- 3 | ## babel-plugin-app-decorators-element-to-function 4 | Babel Plugin for auto generating code 5 | 6 |

    7 | Dependency Status 8 | devDependency Status 9 |

    10 | 11 | ### Installation 12 | 13 | ```sh 14 | $ npm install babel-plugin-app-decorators-element-to-function --save 15 | ``` 16 | 17 | ### Usage 18 | 19 | #### Via `.babelrc` (Recommended) 20 | 21 | **.babelrc** 22 | 23 | ```json 24 | { 25 | "plugins": ["app-decorators-element-to-function"] 26 | } 27 | ``` 28 | 29 | **.babelrc options** 30 | ```js 31 | "plugins": [ 32 | ["app-decorators-element-to-function"] 33 | ] 34 | ``` 35 | 36 | #### Via CLI 37 | 38 | ```sh 39 | $ babel --plugins app-decorators-element-to-function script.js 40 | ``` 41 | 42 | #### Via Node API 43 | 44 | ```js 45 | require('babel').transform('code', { 46 | plugins: ['app-decorators-element-to-function'] 47 | }); 48 | ``` 49 | 50 | ### The goal of this babel-plugin is for app-decorators @component: 51 | 52 | see also: https://github.com/babel/babel/issues/1548 53 | 54 | #### Example 1 55 | input: 56 | ```js 57 | class Foo extends HTMLImageElement { 58 | 59 | } 60 | ``` 61 | 62 | output: 63 | ```js 64 | import _elementToFunc from 'app-decorators-element-to-function'; 65 | 66 | class Foo extends _elementToFunc(HTMLImageElement) { 67 | 68 | } 69 | ``` 70 | 71 | #### Tests 72 | ```bash 73 | git clone https://github.com/SerkanSipahi/app-decorators.git 74 | cd app-decorators/packages/app-decorators-element-to-function 75 | npm install 76 | npm run test 77 | ``` 78 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-element-to-function/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-app-decorators-element-to-function", 3 | "description": "babel plugin for app-decorators to extends an HTMLElement in IE and Safari", 4 | "version": "0.8.252", 5 | "author": "Serkan Sipahi ", 6 | "main": "lib/index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/babel-plugin-app-decorators-element-to-function" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "prepublish": "npm run clean && ./node_modules/.bin/babel src -d lib", 17 | "compile": "npm run srcDir && npm run testDir", 18 | "test": "npm run clean && npm run compile && node tmp/test/index.js", 19 | "inspect": "npm run compile && node --inspect --debug-brk tmp/test/index.js", 20 | "srcDir": "./node_modules/.bin/babel src --out-dir tmp/src --source-maps", 21 | "testDir": "./node_modules/.bin/babel test --out-dir tmp/test --source-maps", 22 | "clean": "rm -rf lib tmp" 23 | }, 24 | "keywords": [ 25 | "parse", 26 | "syle", 27 | "decorator" 28 | ], 29 | "dependencies": { 30 | "app-decorators-simple-it": "0.8.252", 31 | "babel-cli": "6.26.0", 32 | "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", 33 | "should": "13.2.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-element-to-function/src/elements.js: -------------------------------------------------------------------------------- 1 | let elements = { 2 | 'HTMLElement': 'figcaption', 3 | 'HTMLHtmlElement': 'html', 4 | 'HTMLAnchorElement': 'a', 5 | 'HTMLAreaElement': 'area', 6 | 'HTMLBRElement': 'br', 7 | 'HTMLBaseElement': 'base', 8 | 'HTMLBodyElement': 'body', 9 | 'HTMLButtonElement': 'button', 10 | 'HTMLCanvasElement': 'canvas', 11 | 'HTMLContentElement': 'content', 12 | 'HTMLDListElement': 'dl', 13 | 'HTMLDataElement': 'data', 14 | 'HTMLDataListElement': 'datalist', 15 | 'HTMLDivElement': 'div', 16 | 'HTMLDocument': 'document', 17 | 'HTMLFieldSetElement': 'fieldset', 18 | 'HTMLFormElement': 'form', 19 | 'HTMLFrameSetElement': 'framset', 20 | 'HTMLHRElement': 'hr', 21 | 'HTMLHeadElement': 'head', 22 | 'HTMLHeadingElement': 'h', 23 | 'HTMLIFrameElement': 'iframe', 24 | 'HTMLImageElement': 'img', 25 | 'HTMLInputElement': 'input', 26 | 'HTMLKeygenElement': 'keygen', 27 | 'HTMLLIElement': 'li', 28 | 'HTMLLabelElement': 'label', 29 | 'HTMLLegendElement': 'legend', 30 | 'HTMLLinkElement': 'link', 31 | 'HTMLMapElement': 'map', 32 | 'HTMLMediaElement': 'media', 33 | 'HTMLMetaElement': 'meta', 34 | 'HTMLModElement': 'mod', 35 | 'HTMLOListElement': 'ol', 36 | 'HTMLObjectElement': 'object', 37 | 'HTMLOptGroupElement': 'optgroup', 38 | 'HTMLOptionElement': 'option', 39 | 'HTMLOutputElement': 'output', 40 | 'HTMLParagraphElement': 'p', 41 | 'HTMLParamElement': 'param', 42 | 'HTMLPictureElement': 'picture', 43 | 'HTMLPreElement': 'pre', 44 | 'HTMLQuoteElement': 'quote', 45 | 'HTMLScriptElement': 'script', 46 | 'HTMLSelectElement': 'select', 47 | 'HTMLShadowElement': 'shadow', 48 | 'HTMLSpanElement': 'span', 49 | 'HTMLStyleElement': 'style', 50 | 'HTMLTableCaptionElement': 'caption', 51 | 'HTMLTableCellElement': 'td', 52 | 'HTMLTableColElement': 'col', 53 | 'HTMLTableElement': 'table', 54 | 'HTMLTableRowElement': 'tr', 55 | 'HTMLTemplateElement': 'template', 56 | 'HTMLTextAreaElement': 'textarea', 57 | 'HTMLTitleElement': 'title', 58 | 'HTMLUListElement': 'ul', 59 | 'HTMLUnknownElement': 'unknown', 60 | 'HTMLDetailsElement': 'details', 61 | 'HTMLDialogElement': 'dialog', 62 | 'HTMLMeterElement': 'meter', 63 | 'HTMLProgressElement': 'progress', 64 | 'HTMLTimeElement': 'time', 65 | 'SVGElement': 'svg', 66 | 'HTMLAudioElement': 'audio', 67 | 'HTMLEmbedElement': 'embed', 68 | 'HTMLSourceElement': 'source', 69 | 'HTMLTrackElement': 'track', 70 | 'HTMLVideoElement': 'video', 71 | }; 72 | 73 | export { 74 | elements 75 | }; 76 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-element-to-function/src/index.js: -------------------------------------------------------------------------------- 1 | import * as t from "babel-types"; 2 | import { elements } from "./elements"; 3 | 4 | let getProgram = node => { 5 | while(node.parent){ 6 | node = node.findParent(node => node); 7 | if(node.key === "program"){ 8 | return node; 9 | } 10 | } 11 | }; 12 | 13 | let plugin = () => { 14 | 15 | let visitor = { 16 | ClassDeclaration(path) { 17 | 18 | // do nothing when no superClass found 19 | let {superClass} = path.node; 20 | if(!superClass){ 21 | return; 22 | } 23 | 24 | let superClassName = superClass.name; 25 | if(!elements[superClassName]){ 26 | return; 27 | } 28 | 29 | // replace extends identifier with "elementToFunc" 30 | let {name} = path.scope.generateUidIdentifier("elementToFunc"); 31 | let node = t.callExpression(t.identifier(name), [t.identifier(superClassName)]); 32 | path.node.superClass = node; 33 | 34 | // create import statement for elementToFunc 35 | let importDeclaration = t.importDeclaration( 36 | [t.importDefaultSpecifier(t.identifier(name))], 37 | t.stringLiteral("app-decorators/src/libs/element-to-function") 38 | ); 39 | 40 | // add import statement to program body 41 | let program = getProgram(path); 42 | program.node.body.unshift(importDeclaration); 43 | }, 44 | }; 45 | 46 | return { visitor }; 47 | }; 48 | 49 | export default plugin; 50 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-element-to-function/test/index.js: -------------------------------------------------------------------------------- 1 | import { it } from 'app-decorators-simple-it'; 2 | import { transform } from 'babel-core'; 3 | import appDecoratorElementToFunc from '../src/index'; 4 | import 'should'; 5 | 6 | let clean = value => value.replace(/[\r\n ]+/g, '').trim(); 7 | 8 | let transformCode = code => { 9 | 10 | let generated = transform(code, { 11 | plugins: [ 12 | appDecoratorElementToFunc, 13 | ] 14 | }); 15 | 16 | return generated.code; 17 | }; 18 | 19 | it('should modify nothing when no superClass found', () => { 20 | 21 | let actual =`class Foo {}`; 22 | clean(transformCode(actual)).should.be.equal(clean(actual)); 23 | 24 | }); 25 | 26 | it('should modify nothing when superClass is not an HTML Element', () => { 27 | 28 | let actual =`class Foo extends Bar {}`; 29 | clean(transformCode(actual)).should.be.equal(clean(actual)); 30 | 31 | }); 32 | 33 | it('should modify superClass when its an HTML Element and add import statement (1)', () => { 34 | 35 | let actual =`class Foo extends HTMLElement {}`; 36 | let expected =` 37 | import _elementToFunc from "app-decorators/src/libs/element-to-function"; 38 | class Foo extends _elementToFunc(HTMLElement) {}`; 39 | 40 | clean(transformCode(actual)).should.be.equal(clean(expected)); 41 | 42 | }); 43 | 44 | it('should modify superClass when its an HTML Element and add import statement (2)', () => { 45 | 46 | let actual =` 47 | import x from 'x'; 48 | class Foo extends HTMLElement {}`; 49 | 50 | let expected =` 51 | import _elementToFunc from 'app-decorators/src/libs/element-to-function'; 52 | 53 | import x from 'x'; 54 | class Foo extends _elementToFunc(HTMLElement) {}`; 55 | 56 | clean(transformCode(actual)).should.be.equal(clean(expected)); 57 | 58 | }); 59 | 60 | it.run(); 61 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-style-precompile/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-es2015-modules-commonjs", 4 | "transform-function-bind", 5 | "syntax-function-bind" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-style-precompile/README.md: -------------------------------------------------------------------------------- 1 | ## babel-plugin-app-decorators-style-precompile 2 | Babel Plugin for auto generating code 3 | 4 |

    5 | Dependency Status 6 | devDependency Status 7 |

    8 | 9 | ### Installation 10 | 11 | ```sh 12 | $ npm install babel-plugin-app-decorators-style-precompile --save 13 | ``` 14 | 15 | ### Usage 16 | 17 | #### Via `.babelrc` (Recommended) 18 | 19 | **.babelrc** 20 | 21 | ```json 22 | { 23 | "plugins": ["app-decorators-style-precompile"] 24 | } 25 | ``` 26 | 27 | **.babelrc options** 28 | ```js 29 | "plugins": [ 30 | ["app-decorators-style-precompile", { 31 | autoprefixer: "last 2 versions", 32 | minify: true 33 | }] 34 | ] 35 | ``` 36 | 37 | #### Via CLI 38 | 39 | ```sh 40 | $ babel --plugins app-decorators-style-precompile script.js 41 | ``` 42 | 43 | #### Via Node API 44 | 45 | ```js 46 | require('babel').transform('code', { 47 | plugins: ['app-decorators-style-precompile'] 48 | }); 49 | ``` 50 | 51 | ### The goal of this babel-plugin is precompile precomplie the stylesheet inside of @style: 52 | 53 | #### Example 54 | Input: 55 | ```js 56 | @view(` 57 | // this will be loaded async on load event 58 | @on load { 59 | @fetch my/async/styles1.css; 60 | @fetch my/async/styles2.css; 61 | } 62 | // this applied immediately 63 | .my-critical-path-selector { 64 | width: 100px; 65 | height: 100px; 66 | } 67 | `) 68 | class Foo { 69 | 70 | } 71 | ``` 72 | Output: 73 | ```js 74 | @view([ 75 | { 76 | attachOn: "load", 77 | imports: [ 78 | "my/async/styles1.css", 79 | "my/async/styles1.css" 80 | ], 81 | styles: "", 82 | type: "on", 83 | }, 84 | { 85 | attachOn: "immediately", 86 | imports: [], 87 | styles: ".my-critical-path-selector { width: 100px; height: 100px; }", 88 | type: "default", 89 | }, 90 | ]) 91 | class Foo { 92 | 93 | } 94 | ``` 95 | 96 | 97 | #### Tests 98 | ```bash 99 | git clone https://github.com/SerkanSipahi/app-decorators.git 100 | cd app-decorators/packages/babel-plugin-app-decorators-style-precompile 101 | make install 102 | make test 103 | ``` 104 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-style-precompile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-app-decorators-style-precompile", 3 | "description": "babel plugin for app-decorators style decorator", 4 | "version": "0.8.252", 5 | "author": "Serkan Sipahi ", 6 | "main": "lib/index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/babel-plugin-app-decorators-style-precompile" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "prepublish": "npm run clean && ./node_modules/.bin/babel src -d lib --source-maps && rm -f lib/index.test.js", 17 | "test": "npm run clean && ./node_modules/.bin/babel src --out-dir lib --source-maps && node lib/index.test.js", 18 | "compile": "npm run clean && ./node_modules/.bin/babel src --out-dir lib --watch --source-maps", 19 | "inspect": "node --inspect --debug-brk lib/index.test.js", 20 | "clean": "rm -rf lib" 21 | }, 22 | "keywords": [ 23 | "parse", 24 | "syle", 25 | "decorator" 26 | ], 27 | "dependencies": { 28 | "app-decorators-simple-it": "0.8.252", 29 | "assert-diff": "1.2.6", 30 | "babel-cli": "6.26.0", 31 | "babel-plugin-syntax-decorators": "6.13.0", 32 | "babel-plugin-syntax-function-bind": "6.13.0", 33 | "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", 34 | "babel-plugin-transform-function-bind": "6.22.0", 35 | "core-js": "2.5.7", 36 | "postcss-parse-atrule-events": "0.8.252", 37 | "should": "13.2.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-style-precompile/src/index.js: -------------------------------------------------------------------------------- 1 | import * as t from "babel-types"; 2 | import template from 'babel-template'; 3 | import { parse } from 'postcss-parse-atrule-events'; 4 | 5 | /** 6 | * getDecorator 7 | * @param decoratorName {string} 8 | * @returns {*} 9 | */ 10 | let getDecorator = function(decoratorName) { 11 | 12 | if(!this.node.decorators){ 13 | return []; 14 | } 15 | 16 | return this.node.decorators.filter(deco => { 17 | let { name } = deco.expression.callee; 18 | if(name === decoratorName){ 19 | return true; 20 | } 21 | }); 22 | }; 23 | 24 | /** 25 | * getStyle 26 | * @returns {{style: null}} 27 | */ 28 | let getStyle = function(){ 29 | 30 | let statement = { style: null, error: null }; 31 | 32 | let args = this.expression.arguments; 33 | if(!args.length) { 34 | return statement; 35 | } 36 | if(t.isStringLiteral(args[0])){ 37 | let [{ value }] = args; 38 | statement.style = value; 39 | } else if(t.isTemplateLiteral(args[0])){ 40 | let [{ quasis: [{ value: { raw } }] }] = args; 41 | statement.style = raw; 42 | } else if(t.isArrayExpression(args[0])) { 43 | statement.style = args[0]; 44 | } else { 45 | statement.error = ` 46 | Please use StringLiteral ("foo") or TemplateLiteral (\`bar\`). Do not use 47 | something like this: "hello" + "World".`; 48 | } 49 | 50 | return statement; 51 | }; 52 | 53 | let getOptions = function(){ 54 | 55 | let args = this.expression.arguments; 56 | if(!args.length) { 57 | return {}; 58 | } 59 | if(t.isObjectExpression(args[1])){ 60 | return args[1]; 61 | } 62 | 63 | return {}; 64 | }; 65 | 66 | /** 67 | * precompile 68 | * @params {string} 69 | * @params {object} 70 | * @returns {Array} 71 | */ 72 | let precompile = (style, opts = {}) => parse(style, Object.assign({ optimize: true }, opts)); 73 | 74 | /** 75 | * @param collection {array} 76 | * @returns {string} 77 | */ 78 | let arrayToString = collection => { 79 | let importsTpl = ""; 80 | for(let imp of collection) importsTpl += `"${imp}", `; 81 | return importsTpl; 82 | }; 83 | 84 | /** 85 | * createAst 86 | * @this-param {string} 87 | * @param vars {object} 88 | * @returns {object} 89 | */ 90 | let createAst = function(vars = {}){ 91 | 92 | let styleTemplate = ""; 93 | for(let styleObj of this){ 94 | let {attachOn, imports, styles, type, option} = styleObj; 95 | styleTemplate += `{ 96 | attachOn: "${attachOn}", 97 | imports: [${arrayToString(imports)}], 98 | styles: ${styles && styles.length ? "`"+styles+"`" : '"'+styles+'"'}, 99 | type: "${type}", 100 | ${(option) ? "option: ["+arrayToString(option || [])+"]" : ""} 101 | },`; 102 | } 103 | return template(`[${styleTemplate}]`)(); 104 | }; 105 | 106 | function plugin () { 107 | 108 | return { 109 | visitor: { 110 | ClassDeclaration(path, { opts }){ 111 | 112 | let [ component ] = path::getDecorator('style'); 113 | if(!component){ 114 | return; 115 | } 116 | 117 | let { style, error } = component::getStyle(); 118 | if (error) { 119 | throw new Error(error); 120 | } 121 | 122 | if(typeof style !== "string"){ 123 | return; 124 | } 125 | 126 | let options = component::getOptions(); // should be createOptAst 127 | let precompiled = template::precompile(style/*, options*/); 128 | let ast = precompiled::createAst(); 129 | component.expression.arguments.splice(0, 1, ast.expression); 130 | } 131 | } 132 | }; 133 | } 134 | 135 | export default plugin; -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-style-precompile/src/index.test.js: -------------------------------------------------------------------------------- 1 | import { it } from 'app-decorators-simple-it'; 2 | import { transform } from 'babel-core'; 3 | import syntaxDecorator from 'babel-plugin-syntax-decorators'; 4 | import syntaxFuncBind from 'babel-plugin-syntax-function-bind'; 5 | import appDecoratorStylePrecompiler from '../lib/index'; 6 | 7 | import 'should'; 8 | 9 | let clean = value => value.replace(/[\r\n ]+/g, '').trim(); 10 | 11 | function transformCode(code, options = {}){ 12 | 13 | let generated = transform(code, { 14 | plugins: [ 15 | [appDecoratorStylePrecompiler, options], 16 | syntaxDecorator, 17 | syntaxFuncBind 18 | ] 19 | }); 20 | return generated.code; 21 | } 22 | 23 | it('should throw error when String/Template -Literal concatenated by binary expression', () => { 24 | 25 | let actualStringLiteral =` 26 | @style(".foo { background-color: " + "red;") 27 | class Foo {}`; 28 | 29 | let actualTemplateLiteral =` 30 | @style(\`.foo { background-color: \` + \`red;\`) 31 | class Foo {}`; 32 | 33 | (() => { transformCode(actualStringLiteral) }).should.throw(); 34 | (() => { transformCode(actualTemplateLiteral) }).should.throw(); 35 | }); 36 | 37 | it('should not throw error when using interpolation vars', () => { 38 | 39 | let actual =` 40 | let x = 'red'; 41 | @style(\`.color: \${x};\`) 42 | class Foo {}`; 43 | 44 | (() => { transformCode(actual) }).should.not.throw(); 45 | }); 46 | 47 | it('should not throw error when other decorator', () => { 48 | 49 | let actual =` 50 | @xzy(\`.color: red;\`) 51 | class Foo {}`; 52 | 53 | let expected =` 54 | @xzy(\`.color: red;\`) 55 | class Foo {}`; 56 | 57 | clean(transformCode(actual)).should.be.equal(clean(expected)); 58 | }); 59 | 60 | it('should not throw error when passed array object', () => { 61 | 62 | let actual =` 63 | @style([{a:1, b: 2}]) 64 | class Foo {}`; 65 | 66 | let expected =` 67 | @style([{a:1, b: 2}]) 68 | class Foo {}`; 69 | 70 | (() => { transformCode(actual) }).should.not.throw(); 71 | clean(transformCode(actual)).should.be.equal(clean(expected)); 72 | }); 73 | 74 | it('should compile style literal to object literal', () => { 75 | 76 | let actual =` 77 | @style(\` 78 | .foo { 79 | background-color: red; 80 | } 81 | 82 | @on load { 83 | @fetch to/my/src/file.css; 84 | } 85 | @on load { 86 | @fetch to/my/src/file2.css; 87 | } 88 | .bar { 89 | background-color: red; 90 | } 91 | @query (radius: 400px) near (max-width:639px) and (orientation: portrait) { 92 | @fetch load/my/styles11.css; 93 | .foo { 94 | color: red; 95 | } 96 | @fetch load/my/styles99.css; 97 | } 98 | 99 | @fetch to/my/critical/path/file3.css; 100 | 101 | \`) 102 | class Foo {}`; 103 | 104 | let expected =` 105 | @style([ 106 | { 107 | attachOn:"immediately", 108 | imports:[ 109 | "to/my/critical/path/file3.css" 110 | ], 111 | styles:\` 112 | .foo { 113 | background-color:red; 114 | } 115 | .bar { 116 | background-color:red; 117 | }\`, 118 | type:"default" 119 | }, 120 | { 121 | attachOn:"load", 122 | imports:[ 123 | "to/my/src/file.css", 124 | "to/my/src/file2.css" 125 | ], 126 | styles:"", 127 | type:"on" 128 | }, 129 | { 130 | attachOn:"(max-width:639px)-and-(orientation:-portrait) (max-width:639px) and (orientation: portrait)", 131 | imports:[ 132 | "load/my/styles11.css", 133 | "load/my/styles99.css" 134 | ], 135 | styles: \`.foo { color: red; }\`, 136 | type:"query", 137 | option: ["radius", "400px"] 138 | } 139 | ]) 140 | class Foo {}`; 141 | 142 | clean(transformCode(actual)).should.be.equal(clean(expected)); 143 | 144 | }); 145 | 146 | it.skip('should minify css', () => { 147 | 148 | }); 149 | 150 | it.skip('should autoprefix css', () => { 151 | 152 | }); 153 | 154 | it.run(); 155 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "transform-function-bind", 7 | "syntax-function-bind" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerkanSipahi/app-decorators/15f046b1bbe56af0d45b4a1b45a6a4c5689e4763/packages/babel-plugin-app-decorators-view-precompile/CHANGELOG.md -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Serkan Sipahi 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | @echo "" 3 | @echo "Available Targets:" 4 | @echo "" 5 | @echo " make test" 6 | @echo " make compile" 7 | @echo "" 8 | 9 | test: 10 | $(babel-node) $(mocha) -- test 11 | 12 | compile: 13 | $(babel) src -d lib; 14 | 15 | babel = ./node_modules/.bin/babel 16 | babel-node = ./node_modules/.bin/babel-node 17 | mocha = ./node_modules/.bin/_mocha 18 | 19 | .PHONY: test compile; 20 | MAKEFLAGS = -s 21 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/README.md: -------------------------------------------------------------------------------- 1 | ## babel-plugin-app-decorators-view-precompile 2 | Babel Plugin for auto generating code 3 | 4 |

    5 | Dependency Status 6 | devDependency Status 7 |

    8 | 9 | ### Installation 10 | 11 | ```sh 12 | $ npm install babel-plugin-app-decorators-view-precompile --save 13 | ``` 14 | 15 | ### Usage 16 | 17 | #### Via `.babelrc` (Recommended) 18 | 19 | **.babelrc** 20 | 21 | ```json 22 | { 23 | "plugins": ["app-decorators-view-precompile"] 24 | } 25 | ``` 26 | 27 | **.babelrc options** 28 | ```js 29 | "plugins": [ 30 | ["app-decorators-view-precompile", { 31 | "engine": "handlebars" 32 | }] 33 | ] 34 | ``` 35 | 36 | #### Via CLI 37 | 38 | ```sh 39 | $ babel --plugins app-decorators-view-precompile script.js 40 | ``` 41 | 42 | #### Via Node API 43 | 44 | ```js 45 | require('babel').transform('code', { 46 | plugins: ['app-decorators-view-precompile'] 47 | }); 48 | ``` 49 | 50 | ### The goal of this babel-plugin is precompile template with handlebars that is inside of app-decorators @view: 51 | 52 | #### Example 53 | Input: 54 | ```js 55 | @view(` 56 | {{#if foo}}
    Hello World
    57 | {{else}} 58 |
    Hello Mars
    59 | {{/if}} 60 | `) 61 | class Foo { 62 | 63 | } 64 | ``` 65 | Output: 66 | ```js 67 | @view({ 68 | "1": function(container, depth0, helpers, partials, data) { 69 | return "
    Hello World
    \n"; 70 | }, 71 | "3": function(container, depth0, helpers, partials, data) { 72 | return "
    hello Mars
    \n"; 73 | }, 74 | "compiler": [7, ">= 4.0.0"], 75 | "main": function(container, depth0, helpers, partials, data) { 76 | var stack1; 77 | 78 | return "\n " + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {}, (depth0 != null ? depth0.foo : depth0), { 79 | "name": "if", 80 | "hash": {}, 81 | "fn": container.program(1, data, 0), 82 | "inverse": container.program(3, data, 0), 83 | "data": data 84 | })) != null ? stack1 : ""); 85 | }, 86 | "useData": true 87 | }) 88 | class Foo { 89 | 90 | } 91 | ``` 92 | 93 | 94 | #### Tests 95 | ```bash 96 | git clone https://github.com/SerkanSipahi/app-decorators.git 97 | cd app-decorators/packages/babel-plugin-app-decorators-view-precompile 98 | make install 99 | make test 100 | ``` 101 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-app-decorators-view-precompile", 3 | "description": "Babel plugin for app-decorators @view", 4 | "version": "0.8.252", 5 | "author": "Serkan Sipahi ", 6 | "main": "lib/index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/babel-plugin-app-decorators-view-precompile" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "prepublish": "make compile", 17 | "test": "make test" 18 | }, 19 | "keywords": [ 20 | "babel", 21 | "babel-plugin", 22 | "es7", 23 | "decorators", 24 | "view" 25 | ], 26 | "dependencies": { 27 | "babel-core": "6.26.3", 28 | "babel-plugin-syntax-decorators": "6.13.0", 29 | "babel-plugin-syntax-function-bind": "6.13.0", 30 | "babel-plugin-transform-function-bind": "6.22.0", 31 | "babel-preset-es2015": "6.24.1", 32 | "babel-template": "6.26.0", 33 | "babel-types": "6.26.0", 34 | "babylon": "6.18.0", 35 | "handlebars": "4.0.11" 36 | }, 37 | "devDependencies": { 38 | "babel-cli": "6.26.0", 39 | "chai": "4.1.2", 40 | "mocha": "5.2.0", 41 | "should": "13.2.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/src/index.js: -------------------------------------------------------------------------------- 1 | import * as t from "babel-types"; 2 | import template from 'babel-template'; 3 | import handlebars from 'handlebars'; 4 | 5 | let engines = { 6 | handlebars: { 7 | regex: /\{\{.*?\}\}/, 8 | } 9 | }; 10 | 11 | /** 12 | * getDecorator 13 | * @param decoratorName {string} 14 | * @returns {*} 15 | */ 16 | let getDecorator = function(decoratorName) { 17 | 18 | if(!this.node.decorators){ 19 | return []; 20 | } 21 | 22 | return this.node.decorators.filter(deco => { 23 | let { name } = deco.expression.callee; 24 | if(name === decoratorName){ 25 | return true; 26 | } 27 | }); 28 | }; 29 | 30 | /** 31 | * getTemplate 32 | * @returns {{error: null, template: null}} 33 | */ 34 | let getTemplate = function(){ 35 | 36 | let statement = { error: null, template: null }; 37 | 38 | let args = this.expression.arguments; 39 | if(!args.length) { 40 | return statement; 41 | } 42 | if(t.isStringLiteral(args[0])){ 43 | let [{ value }] = args; 44 | statement.template = value; 45 | } else if(t.isTemplateLiteral(args[0])){ 46 | let [{ quasis: [{ value: { raw } }] }] = args; 47 | statement.template = raw; 48 | } else { 49 | statement.error = ` 50 | Please use StringLiteral ("foo") or TemplateLiteral (\`bar\`). Do not use 51 | something like this: "hello" + "World". 52 | `; 53 | } 54 | 55 | return statement; 56 | }; 57 | 58 | /** 59 | * precompile 60 | * @howto precompile: https://github.com/wycats/handlebars.js/issues/1033 61 | * @this-param {string} 62 | * @returns {string} 63 | */ 64 | let precompile = function(engine){ 65 | 66 | let preCompiled = null; 67 | if(engine === 'handlebars'){ 68 | preCompiled = `(${handlebars.precompile(this)})`; 69 | } 70 | 71 | 72 | return preCompiled; 73 | }; 74 | 75 | /** 76 | * createAst 77 | * @this-param {string} 78 | * @param vars {object} 79 | * @returns {ast} 80 | */ 81 | let createAst = function(vars = {}){ 82 | 83 | let compileTemplate = template(this); 84 | return compileTemplate(vars); 85 | }; 86 | 87 | function plugin () { 88 | 89 | return { 90 | visitor: { 91 | ClassDeclaration(path, { opts }){ 92 | 93 | let { engine } = opts; 94 | let { regex } = engines[engine]; 95 | 96 | let [ component ] = path::getDecorator('view'); 97 | if(!component){ 98 | return; 99 | } 100 | 101 | let { error, template } = component::getTemplate(); 102 | 103 | if(error){ 104 | throw new Error(error); 105 | } 106 | 107 | if(!template){ 108 | return; 109 | } 110 | 111 | // do nothing when not template vars e.g. {{foo}} found 112 | if(!regex.test(template)){ 113 | return; 114 | } 115 | 116 | let precompiled = template::precompile(engine); 117 | let ast = precompiled::createAst(); 118 | 119 | component.expression.arguments.splice(0, 1, ast.expression); 120 | } 121 | } 122 | }; 123 | } 124 | 125 | export default plugin; -------------------------------------------------------------------------------- /packages/babel-plugin-app-decorators-view-precompile/test/index.js: -------------------------------------------------------------------------------- 1 | import { transform } from 'babel-core'; 2 | import syntaxDecorator from 'babel-plugin-syntax-decorators'; 3 | import syntaxFuncBind from 'babel-plugin-syntax-function-bind'; 4 | import appDecoratorViewPrecompiler from'../src/index'; 5 | import 'should'; 6 | 7 | function trim(str) { 8 | return str.replace(/^\s+/gm, ''); 9 | } 10 | 11 | const defaultCompilerOptions = { 12 | "engine": "handlebars" 13 | }; 14 | 15 | function transformCode(code, options = defaultCompilerOptions){ 16 | 17 | let generated = transform(code, { 18 | plugins: [ 19 | [appDecoratorViewPrecompiler, options], 20 | syntaxDecorator, 21 | syntaxFuncBind 22 | ] 23 | }); 24 | return generated.code; 25 | } 26 | 27 | describe('@view', () => { 28 | 29 | it('should throw error when String/Template -Literal concatenated by binary expression', () => { 30 | 31 | let actualStringLiteral =` 32 | @view("hello" + "world") 33 | class Foo {}`; 34 | 35 | let actualTemplateLiteral =` 36 | @view(\`hello\` + \`world\`) 37 | class Foo {}`; 38 | 39 | (() => { transformCode(actualStringLiteral) }).should.throw(); 40 | (() => { transformCode(actualTemplateLiteral) }).should.throw(); 41 | }); 42 | 43 | it('should not throw error when using interpolation vars', () => { 44 | 45 | let actual =` 46 | let x = 'foo'; 47 | @view(\`Hello \${x} World\`) 48 | class Foo {}`; 49 | 50 | (() => { transformCode(actual) }).should.not.throw(); 51 | }); 52 | 53 | 54 | it('should do nothing when no template passed', () => { 55 | 56 | let actual =` 57 | @view() 58 | class Foo {}`; 59 | 60 | let expected = ` 61 | @view() 62 | class Foo {}`; 63 | 64 | trim(transformCode(actual)).should.be.equal(trim(expected)); 65 | }); 66 | 67 | it('should not precompile when "StringLiteral" passed without template vars', () => { 68 | 69 | let actual =` 70 | @view("Hello World") 71 | class Foo {}`; 72 | 73 | let expected = ` 74 | @view("Hello World") 75 | class Foo {}`; 76 | 77 | trim(transformCode(actual)).should.be.equal(trim(expected)); 78 | }); 79 | 80 | it('should not precompile when "TemplateLiteral" passed without template vars', () => { 81 | 82 | let actual =` 83 | @view(\`Hello World\`) 84 | class Foo {}`; 85 | 86 | let expected = ` 87 | @view(\`Hello World\`) 88 | class Foo {}`; 89 | 90 | trim(transformCode(actual)).should.be.equal(trim(expected)); 91 | 92 | }); 93 | 94 | it('should precompile when "StringLiteral" passed with template vars', () => { 95 | 96 | let actual =` 97 | @view("Hello {{item}}") 98 | class Foo {}`; 99 | 100 | let expected = ` 101 | @view({ 102 | "compiler": [7, ">= 4.0.0"], 103 | "main": function (container, depth0, helpers, partials, data) { 104 | var helper; 105 | return "Hello " + container.escapeExpression((helper = (helper = helpers.item || (depth0 != null ? depth0.item : depth0)) != null ? helper : helpers.helperMissing, typeof helper === "function" ? helper.call(depth0 != null ? depth0 : container.nullContext || {}, { 106 | "name": "item", 107 | "hash": {}, 108 | "data": data 109 | }) : helper)); 110 | }, 111 | "useData": true 112 | }) 113 | class Foo {}`; 114 | 115 | trim(transformCode(actual)).should.be.equal(trim(expected)); 116 | 117 | }); 118 | 119 | it('should precompile when "StringLiteral" passed with template vars', () => { 120 | 121 | let actual =` 122 | @view(\`Hello {{item}}\`) 123 | class Foo {}`; 124 | 125 | let expected = ` 126 | @view({ 127 | "compiler": [7, ">= 4.0.0"], 128 | "main": function (container, depth0, helpers, partials, data) { 129 | var helper; 130 | return "Hello " + container.escapeExpression((helper = (helper = helpers.item || (depth0 != null ? depth0.item : depth0)) != null ? helper : helpers.helperMissing, typeof helper === "function" ? helper.call(depth0 != null ? depth0 : container.nullContext || {}, { 131 | "name": "item", 132 | "hash": {}, 133 | "data": data 134 | }) : helper)); 135 | }, 136 | "useData": true 137 | }) 138 | class Foo {}`; 139 | 140 | trim(transformCode(actual)).should.be.equal(trim(expected)); 141 | 142 | }); 143 | 144 | }); 145 | -------------------------------------------------------------------------------- /packages/babel-preset-app-decorators/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-es2015-modules-commonjs" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-preset-app-decorators/.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = tab 3 | indent_size = 4 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true -------------------------------------------------------------------------------- /packages/babel-preset-app-decorators/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | @echo "" 3 | @echo "Available Targets:" 4 | @echo "" 5 | @echo " make publish (npm)" 6 | @echo "" 7 | 8 | publish: clean compile npm-publish 9 | 10 | clean: 11 | rm -rf lib/* 12 | 13 | compile: 14 | $(babel) src -d lib; 15 | 16 | npm-publish: 17 | npm publish 18 | 19 | babel = ./node_modules/.bin/babel 20 | 21 | .PHONY: publish clean compile npm-publish; 22 | MAKEFLAGS = -s 23 | -------------------------------------------------------------------------------- /packages/babel-preset-app-decorators/README.md: -------------------------------------------------------------------------------- 1 | # babel-preset-app-decorators 2 | 3 | > Babel preset for app-decorators. 4 | 5 | babel-preset-app-decorators Babeljs v6.x 6 | 7 |

    8 | Dependency Status 9 | devDependency Status 10 |

    11 | 12 | 13 | ## Install 14 | 15 | ```sh 16 | $ npm install --save-dev babel-preset-app-decorators 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### Via `.babelrc` (Recommended) 22 | 23 | **.babelrc** 24 | 25 | ```json 26 | { 27 | "presets": ["app-decorators"] 28 | } 29 | ``` 30 | 31 | ### Via CLI 32 | 33 | ```sh 34 | $ babel script.js --presets app-decorators 35 | ``` 36 | 37 | ### Via Node API 38 | 39 | ```javascript 40 | require("babel-core").transform("code", { 41 | presets: ["app-decorators"] 42 | }); 43 | ``` -------------------------------------------------------------------------------- /packages/babel-preset-app-decorators/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-preset-app-decorators", 3 | "version": "0.8.252", 4 | "description": "custom babel-preset for app-decorators", 5 | "main": "lib/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/babel-preset-app-decorators" 9 | }, 10 | "keywords": [ 11 | "babel-preset", 12 | "app-decorators" 13 | ], 14 | "author": "Serkan Sipahi ", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 18 | }, 19 | "scripts": { 20 | "prepublish": "make compile", 21 | "test": "echo 'no tests required'" 22 | }, 23 | "homepage": "https://github.com/SerkanSipahi/babel-preset-app-decorators#readme", 24 | "dependencies": { 25 | "babel-plugin-app-decorators-component": "0.8.252", 26 | "babel-plugin-app-decorators-component-register": "0.8.252", 27 | "babel-plugin-app-decorators-style-precompile": "0.8.252", 28 | "babel-plugin-app-decorators-view-precompile": "0.8.252", 29 | "babel-plugin-transform-class-properties": "6.24.1", 30 | "babel-plugin-transform-decorators-legacy": "1.3.5", 31 | "babel-plugin-transform-function-bind": "6.22.0" 32 | }, 33 | "devDependencies": { 34 | "babel-cli": "6.26.0", 35 | "babel-plugin-transform-es2015-modules-commonjs": "6.26.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/babel-preset-app-decorators/src/index.js: -------------------------------------------------------------------------------- 1 | import transformAppDecComponent from "babel-plugin-app-decorators-component"; 2 | import transformAppDecComponentRegister from 'babel-plugin-app-decorators-component-register'; 3 | import transformAppDecViewPrecompiler from 'babel-plugin-app-decorators-view-precompile'; 4 | import transformAppDecStylePrecompiler from 'babel-plugin-app-decorators-style-precompile'; 5 | import transformDecoratorsLegacy from "babel-plugin-transform-decorators-legacy"; 6 | import transformClassProperties from "babel-plugin-transform-class-properties"; 7 | import transformFunctionBind from "babel-plugin-transform-function-bind"; 8 | 9 | export default { 10 | plugins: [ 11 | [transformAppDecComponent, { 12 | "elementToFunc": true, 13 | }], 14 | [transformAppDecComponentRegister, { 15 | "storage_pointer": '@component', 16 | "imports": [ 17 | { "IMPORT_NAME": "Register", "SOURCE": "app-decorators/src/libs/customelement" }, 18 | { "IMPORT_NAME": "storage", "SOURCE": "app-decorators/src/libs/random-storage" }, 19 | ], 20 | }], 21 | [transformAppDecViewPrecompiler, { 22 | "engine": "handlebars" 23 | }], 24 | [transformAppDecStylePrecompiler, { 25 | "minify": true, 26 | "normalize": true, 27 | "autoprefixer": [], 28 | }], 29 | transformDecoratorsLegacy, 30 | transformClassProperties, 31 | transformFunctionBind, 32 | ] 33 | }; -------------------------------------------------------------------------------- /packages/postcss-parse-atrule-events/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-es2015-modules-commonjs"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/postcss-parse-atrule-events/README.md: -------------------------------------------------------------------------------- 1 | ## postcss-parse-atrule-events 2 | Postcss plugin for auto generating code 3 | 4 |

    5 | Dependency Status 6 | devDependency Status 7 |

    8 | 9 | ### Installation 10 | 11 | ```sh 12 | $ npm install postcss-parse-atrule-events --save 13 | ``` 14 | 15 | ### Usage 16 | 17 | #### Via Node API 18 | 19 | ```js 20 | let result = require('postcss-parse-atrule-events').parse('code'); 21 | ``` 22 | 23 | #### Example 24 | Input: 25 | ```css 26 | @on load { 27 | @fetch my/async/styles1.css; 28 | @fetch my/async/styles1.css; 29 | } 30 | .my-critical-path-selector { 31 | width: 100px; 32 | height: 100px; 33 | } 34 | ``` 35 | Output: 36 | ```js 37 | let output = [ 38 | { 39 | attachOn: "load", 40 | imports: [ 41 | "my/async/styles1.css", 42 | "my/async/styles1.css" 43 | ], 44 | styles: "", 45 | type: "on", 46 | }, 47 | { 48 | attachOn: "immediately", 49 | imports: [], 50 | styles: ".my-critical-path-selector { width: 100px; height: 100px; }", 51 | type: "default", 52 | }, 53 | ]; 54 | ``` 55 | 56 | 57 | #### Tests 58 | ```bash 59 | git clone https://github.com/SerkanSipahi/app-decorators.git 60 | cd app-decorators/packages/postcss-parse-atrule-events 61 | npm run test 62 | ``` 63 | -------------------------------------------------------------------------------- /packages/postcss-parse-atrule-events/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-parse-atrule-events", 3 | "description": "postcss plugin for parsing atrule events", 4 | "version": "0.8.252", 5 | "author": "Serkan Sipahi ", 6 | "main": "lib/index.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/SerkanSipahi/app-decorators/tree/master/packages/postcss-parse-atrule-events" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/SerkanSipahi/app-decorators/issues" 14 | }, 15 | "scripts": { 16 | "prepublish": "npm run clean && ./node_modules/.bin/babel src -d lib --source-maps && rm -f lib/index.test.js", 17 | "test": "npm run clean && ./node_modules/.bin/babel src --out-dir lib --source-maps && node lib/index.test.js", 18 | "compile": "npm run clean && ./node_modules/.bin/babel src --out-dir lib --watch --source-maps", 19 | "inspect": "node --inspect --debug-brk lib/index.test.js", 20 | "clean": "rm -rf lib" 21 | }, 22 | "keywords": [ 23 | "postcss", 24 | "parse atrule events" 25 | ], 26 | "dependencies": { 27 | "app-decorators-simple-it": "0.8.252", 28 | "assert-diff": "1.2.6", 29 | "autoprefixer": "8.6.2", 30 | "babel-cli": "6.26.0", 31 | "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", 32 | "core-js": "2.5.7", 33 | "cssnano": "3.10.0", 34 | "postcss": "6.0.22", 35 | "postcss-nested": "3.0.0", 36 | "postcss-normalize": "5.0.0", 37 | "regex-css-media-query": "1.0.0", 38 | "should": "13.2.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/apps/pubsub.js: -------------------------------------------------------------------------------- 1 | 2 | import { Pubsub } from '../libs/pubsub'; 3 | 4 | Pubsub.create = function(config = {}){ 5 | 6 | }; 7 | 8 | export { 9 | Pubsub 10 | }; 11 | -------------------------------------------------------------------------------- /src/apps/router.js: -------------------------------------------------------------------------------- 1 | import { Eventhandler} from '../libs/eventhandler'; 2 | import { Router } from '../libs/router'; 3 | import { extend } from '../libs/dependencies'; 4 | import { routerConfig } from '../configs/route.config.client'; 5 | 6 | /** 7 | * create 8 | * @param {Object} config 9 | * @return {Router} router 10 | */ 11 | Router.create = function(config = {}){ 12 | 13 | let _routerConfig = Router.makeConfig(config); 14 | return new Router(_routerConfig); 15 | }; 16 | 17 | /** 18 | * makeConfig 19 | * @param config 20 | * @return {object} _routerConfig 21 | */ 22 | Router.makeConfig = function(config = {}){ 23 | 24 | let _routerConfig = extend( 25 | true, {}, 26 | routerConfig, 27 | config 28 | ); 29 | 30 | _routerConfig.scope = new Eventhandler({ 31 | element: _routerConfig.scope, 32 | }); 33 | _routerConfig.globalScope = new Eventhandler({ 34 | element: _routerConfig.globalScope, 35 | }); 36 | 37 | return _routerConfig; 38 | }; 39 | 40 | export { 41 | Router 42 | }; 43 | -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | import { polyfills } from './datas/polyfills'; 2 | import { lazyPolyfillLoader} from './helpers/lazyPolyfillLoader'; 3 | 4 | 5 | let bootstrapPolyfills = lazyPolyfillLoader(System, polyfills); 6 | 7 | export { 8 | bootstrapPolyfills 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/configs/elements.js: -------------------------------------------------------------------------------- 1 | let elements = { 2 | 3 | 'HTMLElement@figcation' : 'figcaption', 4 | 'HTMLElement@figure' : 'figure', 5 | 'HTMLElement@footer' : 'footer', 6 | 'HTMLElement@header' : 'header', 7 | 'HTMLElement@main' : 'main', 8 | 'HTMLElement@mark' : 'mark', 9 | 'HTMLElement@article' : 'article', 10 | 'HTMLElement@aside' : 'aside', 11 | 'HTMLElement@bdi' : 'bdi', 12 | 'HTMLElement@nav' : 'nav', 13 | 'HTMLElement@rp' : 'rp', 14 | 'HTMLElement@rt' : 'rt', 15 | 'HTMLElement@ruby' : 'ruby', 16 | 'HTMLElement@section' : 'section', 17 | 'HTMLElement@summary' : 'summary', 18 | 'HTMLElement@wbr' : 'wbr', 19 | 20 | 'HTMLHtmlElement@html' : 'html', 21 | 'HTMLAnchorElement@a' : 'a', 22 | 'HTMLAreaElement@area' : 'area', 23 | 'HTMLBRElement@br' : 'br', 24 | 'HTMLBaseElement@base' : 'base', 25 | 'HTMLBodyElement@body' : 'body', 26 | 'HTMLButtonElement@button' : 'button', 27 | 'HTMLCanvasElement@canvas' : 'canvas', 28 | 'HTMLContentElement@content' : 'content', 29 | 'HTMLDListElement@dl' : 'dl', 30 | 'HTMLDataElement@data' : 'data', 31 | 'HTMLDataListElement@datalist' : 'datalist', 32 | 'HTMLDivElement@div' : 'div', 33 | 'HTMLDocument@document' : 'document', 34 | 'HTMLFieldSetElement@fieldset' : 'fieldset', 35 | 'HTMLFormElement@form' : 'form', 36 | 'HTMLFrameSetElement@framset' : 'framset', 37 | 'HTMLHRElement@hr' : 'hr', 38 | 'HTMLHeadElement@head' : 'head', 39 | 'HTMLHeadingElement@h' : 'h', 40 | 'HTMLIFrameElement@iframe' : 'iframe', 41 | 'HTMLImageElement@img' : 'img', 42 | 'HTMLInputElement@input' : 'input', 43 | 'HTMLKeygenElement@keygen' : 'keygen', 44 | 'HTMLLIElement@li' : 'li', 45 | 'HTMLLabelElement@label' : 'label', 46 | 'HTMLLegendElement@legend' : 'legend', 47 | 'HTMLLinkElement@link' : 'link', 48 | 'HTMLMapElement@map' : 'map', 49 | 'HTMLMediaElement@media' : 'media', 50 | 'HTMLMetaElement@meta' : 'meta', 51 | 'HTMLModElement@mod' : 'mod', 52 | 'HTMLOListElement@ol' : 'ol', 53 | 'HTMLObjectElement@object' : 'object', 54 | 'HTMLOptGroupElement@optgroup' : 'optgroup', 55 | 'HTMLOptionElement@option' : 'option', 56 | 'HTMLOutputElement@output' : 'output', 57 | 'HTMLParagraphElement@p' : 'p', 58 | 'HTMLParamElement@param' : 'param', 59 | 'HTMLPictureElement@picture' : 'picture', 60 | 'HTMLPreElement@pre' : 'pre', 61 | 'HTMLQuoteElement@quote' : 'quote', 62 | 'HTMLScriptElement@script' : 'script', 63 | 'HTMLSelectElement@select' : 'select', 64 | 'HTMLShadowElement@shadow' : 'shadow', 65 | 'HTMLSpanElement@span' : 'span', 66 | 'HTMLStyleElement@style' : 'style', 67 | 'HTMLTableCaptionElement@caption' : 'caption', 68 | 'HTMLTableCellElement@td' : 'td', 69 | 'HTMLTableColElement@col' : 'col', 70 | 'HTMLTableElement@table' : 'table', 71 | 'HTMLTableRowElement@tr' : 'tr', 72 | 'HTMLTemplateElement@template' : 'template', 73 | 'HTMLTextAreaElement@textarea' : 'textarea', 74 | 'HTMLTitleElement@title' : 'title', 75 | 'HTMLUListElement@ul' : 'ul', 76 | 'HTMLUnknownElement@unknown' : 'unknown', 77 | 'HTMLDetailsElement@details' : 'details', 78 | 'HTMLDialogElement@dialog' : 'dialog', 79 | 'HTMLUnknownElement@menuitem' : 'menuitem', 80 | 'HTMLMeterElement@meter' : 'meter', 81 | 'HTMLProgressElement@progress' : 'progress', 82 | 'HTMLTimeElement@time' : 'time', 83 | 84 | 'SVGElement@svg' : 'svg', 85 | 'HTMLAudioElement@audio' : 'audio', 86 | 'HTMLEmbedElement@embed' : 'embed', 87 | 'HTMLSourceElement@source' : 'source', 88 | 'HTMLTrackElement@track' : 'track', 89 | 'HTMLVideoElement@video' : 'video', 90 | }; 91 | 92 | export { 93 | elements 94 | }; 95 | -------------------------------------------------------------------------------- /src/configs/route.config.client.js: -------------------------------------------------------------------------------- 1 | import { RegExp } from '../libs/dependencies'; 2 | import { queryString } from '../helpers/queryString'; 3 | import { guid } from '../helpers/guid'; 4 | 5 | let routerConfig = { 6 | 7 | routes: [], 8 | 9 | scope: document.body, 10 | globalScope: window, 11 | bind : null, 12 | 13 | event: { 14 | action : 'click a', 15 | popstate : 'popstate', 16 | urlchange: 'urlchange', 17 | }, 18 | mode: { 19 | shadowRoute: false, 20 | }, 21 | helper: { 22 | RegExp : RegExp, 23 | queryString: queryString, 24 | guid : guid, 25 | }, 26 | }; 27 | 28 | export { 29 | routerConfig, 30 | } -------------------------------------------------------------------------------- /src/configs/window-events.js: -------------------------------------------------------------------------------- 1 | 2 | let w = System.global.window; 3 | 4 | let windowEvents = { 5 | 6 | 'afterprint': w, 7 | 'beforeprint': w, 8 | 'beforeunload': w, 9 | 'hashchange': w, 10 | 'load': w, 11 | 'message': w, 12 | 'pagehide': w, 13 | 'pageshow': w, 14 | 'popstate': w, 15 | 'resize': w, 16 | 'storage': w, 17 | 'unload': w, 18 | 19 | 'message': w, 20 | 'mousewheel': w, 21 | 'online': w, 22 | 'offline': w, 23 | 'wheel': w, 24 | 25 | 'keydown': w, 26 | 'keypress': w, 27 | 'keyup': w, 28 | 29 | }; 30 | 31 | export { 32 | windowEvents 33 | }; 34 | -------------------------------------------------------------------------------- /src/datas/init-maps.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We setup a map for each component when is required. E.g when a component 3 | * just use @action and @view it will setup map for only these components 4 | */ 5 | 6 | /** 7 | * initCoreMap 8 | * @param storage {WeakMap} 9 | * @param Class {function} 10 | */ 11 | let initCoreMap = (storage, Class) => { 12 | 13 | if(!storage.has(Class)){ 14 | storage.set(Class, new Map([ 15 | ['@callbacks', new Map([ 16 | ['created', []], 17 | ['attached', []], 18 | ['detached', []], 19 | ])], 20 | ])); 21 | } 22 | }; 23 | 24 | /** 25 | * initActionMap 26 | * @param storage {WeakMap} 27 | * @param Class {function} 28 | */ 29 | let initActionMap = (storage, Class) => { 30 | 31 | let map = storage.get(Class); 32 | if(!map.has('@action')){ 33 | map.set('@action', new Map([ 34 | ["events", []], 35 | ["callbacksDefined", false], 36 | ])); 37 | } 38 | }; 39 | 40 | /** 41 | * initOnMap 42 | * @param storage {WeakMap} 43 | * @param Class {function} 44 | */ 45 | let initOnMap = (storage, Class) => { 46 | 47 | let map = storage.get(Class); 48 | if(!map.has('@on')){ 49 | map.set('@on', new Map([ 50 | ["events", new Map([ 51 | ["local", []], 52 | ["context", []], 53 | ])], 54 | ["callbacksDefined", false], 55 | ])) 56 | } 57 | }; 58 | 59 | /** 60 | * initViewMap 61 | * @param storage {WeakMap} 62 | * @param Class {function} 63 | */ 64 | let initViewMap = (storage, Class) => { 65 | 66 | let map = storage.get(Class); 67 | if(!map.has('@view')){ 68 | map.set('@view', new Map([ 69 | ["bind", []], 70 | ["callbacksDefined", false], 71 | ])); 72 | } 73 | }; 74 | 75 | /** 76 | * initStyleMap 77 | * @param storage {WeakMap} 78 | * @param Class {function} 79 | */ 80 | let initStyleMap = (storage, Class) => { 81 | 82 | let map = storage.get(Class); 83 | if(!map.has('@style')){ 84 | map.set('@style', new Map([ 85 | ["stylesheets", null], 86 | ["callbacksDefined", false], 87 | ["referenceNodes", new Map()], 88 | ["stylesheetStates", []], 89 | ])); 90 | } 91 | }; 92 | 93 | export { 94 | initCoreMap, 95 | initActionMap, 96 | initOnMap, 97 | initViewMap, 98 | initStyleMap, 99 | } -------------------------------------------------------------------------------- /src/datas/polyfills.js: -------------------------------------------------------------------------------- 1 | /** 2 | * modules 3 | * @resources: 4 | * - hayato.io/2016/shadowdomv1/ 5 | * @type {*[]} 6 | */ 7 | let polyfills = [ 8 | [Object, 'assign', 'node_modules/core-js/library/fn/object/assign'], 9 | [Object, 'values', 'node_modules/core-js/library/fn/object/values'], 10 | [Object, 'entries', 'node_modules/core-js/library/fn/object/entries'], 11 | [document, 'registerElement', 'node_modules/webcomponents.js/webcomponents-lite', true], 12 | [window, 'Reflect', 'node_modules/core-js/library/fn/reflect'], 13 | [window, 'WeakMap', 'node_modules/core-js/library/fn/weak-map'], 14 | [window, 'Map', 'node_modules/core-js/library/fn/map'], 15 | [Element.prototype, 'after', 'node_modules/dom4/build/dom4', true], 16 | ]; 17 | 18 | export { 19 | polyfills, 20 | } -------------------------------------------------------------------------------- /src/decorators/action.js: -------------------------------------------------------------------------------- 1 | import { Router } from '../apps/router'; 2 | import { initCoreMap, initActionMap } from '../datas/init-maps'; 3 | import { storage } from '../libs/random-storage'; 4 | 5 | /** 6 | * action 7 | * @param {string} route 8 | * @return {Function} 9 | */ 10 | function action(route) { 11 | 12 | if(!route){ 13 | throw new Error('Please pass an action'); 14 | } 15 | 16 | return ({ constructor }, method, { value }) => { 17 | 18 | let Class = constructor; 19 | 20 | initCoreMap(storage, Class); 21 | initActionMap(storage, Class); 22 | 23 | let map = storage.get(Class); 24 | 25 | map.get('@action').get('events').push([ 26 | `${method} ${route}`, value 27 | ]); 28 | 29 | /** 30 | * ### Ensure "registerCallback('created', ..." (see below) registered only once ### 31 | * This function will called every time if an event registered e.g. @on('click .foo') 32 | * but registerOnCreatedCallback can only call once because we want only Create 33 | * one Eventhandler 34 | */ 35 | if(map.get('@action').get('callbacksDefined')){ 36 | return; 37 | } 38 | 39 | map.get('@callbacks').get('created').push(domNode => { 40 | 41 | let events = map.get('@action').get('events'); 42 | domNode.$router = Router.create({ 43 | routes : events, 44 | scope : domNode, 45 | bind : domNode, 46 | }); 47 | }); 48 | 49 | map.get('@callbacks').get('attached').push(domNode => { 50 | 51 | if(domNode.$router.initialized()){ 52 | return; 53 | } 54 | 55 | let events = map.get('@action').get('events'); 56 | let config = Router.makeConfig({ 57 | routes : events, 58 | scope : domNode, 59 | bind : domNode, 60 | }); 61 | 62 | /** 63 | * Using the same instance is 30% faster in 64 | * (Chrome, Opera) and no difference in Firefox 65 | * @see: https://jsperf.com/new-class-vs-singleton 66 | */ 67 | domNode.$router.reinit(config); 68 | 69 | }); 70 | 71 | map.get('@callbacks').get('detached').push(domNode => { 72 | domNode.$router.destroy(); 73 | }); 74 | 75 | map.get('@action').set('callbacksDefined', true); 76 | } 77 | } 78 | 79 | export { 80 | action 81 | }; -------------------------------------------------------------------------------- /src/decorators/component.js: -------------------------------------------------------------------------------- 1 | import { trigger } from '../helpers/trigger'; 2 | import { initCoreMap } from '../datas/init-maps'; 3 | import { storage } from '../libs/random-storage'; 4 | 5 | /** 6 | * customElementHooks 7 | * @type {string[]} 8 | */ 9 | let customElementHooks = [ 10 | 'created', 11 | 'attached', 12 | 'detached', 13 | 'attributeChanged', 14 | ]; 15 | 16 | /** 17 | * getComponentName 18 | * @returns {string} name 19 | */ 20 | function getComponentName() { 21 | 22 | let localName = this.localName; 23 | let isValue = this.getAttribute('is'); 24 | 25 | return localName + (isValue ? `[is="${isValue}"]` : ''); 26 | } 27 | 28 | /** 29 | * init 30 | * @param callbackName {string} 31 | * @param callbacks {function[]} 32 | * @param vars {object} 33 | */ 34 | function init(callbackName, callbacks = [() => {}], vars){ 35 | 36 | callbacks.forEach(cb => cb(this, vars)); 37 | 38 | this[callbackName]? this[callbackName](vars): null; 39 | this::trigger(callbackName, vars); 40 | } 41 | 42 | /** 43 | * component {function} 44 | * @param {object} settings 45 | */ 46 | function component(settings = {}) { 47 | 48 | /** 49 | * decorator 50 | * @param {function} ComponentClass 51 | */ 52 | return Class => { 53 | 54 | initCoreMap(storage, Class); 55 | 56 | let map = storage.get(Class); 57 | map.set('@component', settings); 58 | 59 | for(let callbackName of customElementHooks){ 60 | Class.prototype[`${callbackName}Callback`] = function(vars){ 61 | let callbacks = map.get('@callbacks').get(callbackName); 62 | this::init(callbackName, callbacks, vars); 63 | }; 64 | } 65 | } 66 | } 67 | 68 | export { 69 | init, 70 | component, 71 | getComponentName, 72 | }; 73 | -------------------------------------------------------------------------------- /src/decorators/model.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * model 4 | * @param {Any} ...args 5 | * @return {Function} 6 | */ 7 | function model(...args) { 8 | return function(target, name, descriptor) { 9 | 10 | } 11 | }; 12 | 13 | export { 14 | model 15 | }; 16 | -------------------------------------------------------------------------------- /src/decorators/modelpool.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * on (EventHandler) 4 | * @param {Any} ...args 5 | * @return {Function} 6 | */ 7 | function modelpool(...args) { 8 | return function(target, name, descriptor) { 9 | 10 | } 11 | } 12 | 13 | export { 14 | modelpool 15 | }; 16 | -------------------------------------------------------------------------------- /src/decorators/on.js: -------------------------------------------------------------------------------- 1 | import { Eventhandler } from '../libs/eventhandler'; 2 | import { namespace } from '../helpers/namespace'; 3 | import { initCoreMap, initOnMap } from '../datas/init-maps'; 4 | import { storage } from '../libs/random-storage'; 5 | 6 | /** 7 | * on (EventHandler) 8 | * @param {string} eventDomain 9 | * @params {string} listenerElement 10 | * @return {Function} 11 | */ 12 | function on(eventDomain, listenerElement = 'local') { 13 | 14 | if(!eventDomain){ 15 | throw new Error('Please pass an event type e.g "click"'); 16 | } 17 | 18 | return ({ constructor }, method, { value }) => { 19 | 20 | let Class = constructor; 21 | 22 | initCoreMap(storage, Class); 23 | initOnMap(storage, Class); 24 | 25 | let mapClassOn = storage.get(Class); 26 | let handler = value; 27 | let eventsMap = mapClassOn.get('@on').get('events'); 28 | 29 | if(listenerElement === 'local'){ 30 | eventsMap.get('local').push([ 31 | eventDomain, handler 32 | ]); 33 | } else { 34 | eventsMap.get('context').push([ 35 | eventDomain, [ handler, listenerElement ] 36 | ]); 37 | } 38 | 39 | /** 40 | * ### Ensure "registerCallback('created', ..." (see below) registered only once ### 41 | * This function will called every time if an event will be registered e.g. over @on('click .foo') 42 | * but registerOnCreatedCallback can only call once because we want only Create 43 | * one Eventhandler 44 | */ 45 | if(mapClassOn.get('@on').get('callbacksDefined')){ 46 | return; 47 | } 48 | 49 | mapClassOn.get('@callbacks').get('created').push(domNode => { 50 | 51 | // register local (domNode) events e.g. thats depends on element click mousemove, etc 52 | let eventsLocal = mapClassOn.get('@on').get('events').get('local'); 53 | if(eventsLocal.length){ 54 | let eventHandler = on.helper.createLocalEventHandler(eventsLocal, domNode); 55 | namespace.create(domNode, '$eventHandler.local', eventHandler); 56 | } 57 | 58 | // register custom events e.g. thats depend on e.g. window onresize, onwheel, etc 59 | let eventsContext = mapClassOn.get('@on').get('events').get('context'); 60 | if(eventsContext.length){ 61 | on.helper.createCustomEventHandler(eventsContext, domNode, (eventHandler, name) => { 62 | namespace.create(domNode, `$eventHandler.${name}`, eventHandler); 63 | }); 64 | } 65 | 66 | }); 67 | 68 | mapClassOn.get('@callbacks').get('attached').push(domNode => { 69 | 70 | // this behavior ensures that when the domNode is 71 | // attached, detached, attached 72 | let eventHandlerLength = 0; 73 | let $eventHandler = Object.values(domNode.$eventHandler); 74 | $eventHandler.forEach(eventHandler => { 75 | if(eventHandler.initialized()){ 76 | eventHandlerLength++ 77 | } 78 | }); 79 | 80 | if(eventHandlerLength === $eventHandler.length){ 81 | return; 82 | } 83 | 84 | Object.entries(domNode.$eventHandler).forEach(([name, eventHandler]) => { 85 | 86 | /** 87 | * Using the same instance is 30% faster in 88 | * (Chrome, Opera) and no difference in Firefox 89 | * @see: https://jsperf.com/new-class-vs-singleton 90 | */ 91 | eventHandler.reinit({ 92 | events: mapClassOn.get('@on').get('events').get(name), 93 | element: domNode, 94 | bind: domNode, 95 | }); 96 | }); 97 | 98 | }); 99 | 100 | mapClassOn.get('@callbacks').get('detached').push(domNode => { 101 | 102 | Object.values(domNode.$eventHandler).forEach(eventHandler => { 103 | eventHandler.destroy(); 104 | }); 105 | }); 106 | 107 | mapClassOn.get('@on').set('callbacksDefined', true); 108 | 109 | } 110 | } 111 | 112 | /***************************************** 113 | * ########## Decorator Helper ########### 114 | *****************************************/ 115 | 116 | on.helper = { 117 | 118 | /** 119 | * createLocalEventHandler 120 | * @param {object} localScopeEvents 121 | * @param {HTMLElement} domNode 122 | * @return {Eventhandler} eventHandler 123 | */ 124 | createLocalEventHandler(localScopeEvents, domNode){ 125 | 126 | return new Eventhandler({ 127 | events: localScopeEvents, 128 | element: domNode, 129 | bind: domNode, 130 | }); 131 | }, 132 | 133 | /** 134 | * createCustomEventHandler 135 | * @param {object} eventsEntries 136 | * @param {HTMLElement} domNode 137 | * @param {function} callback 138 | * @return {HTMLElement} domNode 139 | */ 140 | createCustomEventHandler(eventsEntries, domNode, callback = () => {}) { 141 | 142 | for(let eventEntry of eventsEntries){ 143 | 144 | let [ event, [ handler, node ] ] = eventEntry; 145 | let eventHandler = new Eventhandler({ element: node, bind: domNode }); 146 | let contextName = Object.prototype.toString.call(node).slice(8, -1).toLowerCase(); 147 | let eventHandlerName = `${event}_${contextName}_${handler.name}`; 148 | 149 | eventHandler.on(event, handler); 150 | callback(eventHandler, eventHandlerName); 151 | } 152 | 153 | return domNode; 154 | }, 155 | 156 | }; 157 | 158 | export { 159 | on 160 | }; -------------------------------------------------------------------------------- /src/decorators/pubsub.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * publish 4 | * @param {Any} ...args 5 | * @return {Function} 6 | */ 7 | function publish(...args) { 8 | return function(target, name, descriptor) { 9 | 10 | } 11 | } 12 | 13 | /** 14 | * subscribe 15 | * @param {Any} ...args 16 | * @return {Function} 17 | */ 18 | function subscribe(...args) { 19 | return function(target, name, descriptor) { 20 | 21 | } 22 | } 23 | 24 | export { 25 | publish, 26 | subscribe 27 | }; 28 | -------------------------------------------------------------------------------- /src/decorators/style.js: -------------------------------------------------------------------------------- 1 | import { Stylesheet } from '../libs/stylesheet'; 2 | import { initCoreMap, initStyleMap } from '../datas/init-maps'; 3 | import { storage } from '../libs/random-storage'; 4 | import { Eventhandler } from '../libs/eventhandler'; 5 | import { Router } from '../apps/router'; 6 | 7 | /** 8 | * 9 | * @param style {Object} 10 | * @param element {Element} 11 | * @returns {*} 12 | */ 13 | let getHandler = (style, element) => { 14 | 15 | let {type} = style; 16 | 17 | // default state is immediately 18 | if(type === "on" || type === "default") { 19 | return new Eventhandler({ element }); 20 | } else if(type === "action") { 21 | 22 | //style.attachOn += ` ${style.attachOn}`; 23 | let router = Router.create({ 24 | scope: element 25 | }); 26 | router.start(); 27 | return router; 28 | } else if(type === "query") { 29 | // https://wicg.github.io/ResizeObserver/ 30 | return { 31 | on: () => {}, 32 | off: () => {}, 33 | } 34 | } 35 | }; 36 | 37 | /** 38 | * @param stylesheets {Array} 39 | * @param stylesheetStates {Array} 40 | */ 41 | let syncStylesheetStates = (stylesheets, stylesheetStates) => { 42 | stylesheetStates = []; 43 | stylesheets.forEach(stylesheet => stylesheetStates.push(stylesheet.alreadyDone)); 44 | return stylesheetStates; 45 | }; 46 | 47 | /** 48 | * @param object {Object} 49 | * @param key {string} 50 | * @param value {*} 51 | */ 52 | let setPath = (object, key, value) => { 53 | if(!(object[key] && object[key].length)){ 54 | object[key] = value; 55 | } 56 | }; 57 | 58 | /** 59 | * @param Class {function} 60 | * @param styles {Array} 61 | * @param domNode {{$stylesheets: Array}} 62 | * @param options {{fallback: Function}} 63 | * @param stylesheetStates {Array} 64 | * @returns domNode {Element} 65 | */ 66 | let createStylesheet = (Class, styles, domNode, options, stylesheetStates) => { 67 | 68 | for(let i = 0, len = styles.length; i < len; i++){ 69 | let style = styles[i]; 70 | let stylesheet = new Stylesheet({ 71 | appendTo: domNode, 72 | attachOn: style.attachOn, 73 | imports: style.imports, 74 | styles: style.styles, 75 | type: style.type, 76 | order: i, 77 | alreadyDone: stylesheetStates[i], 78 | fallback: options.fallback, 79 | eventFactory: element => getHandler(style, element), 80 | }); 81 | 82 | domNode.$stylesheets.push(stylesheet); 83 | } 84 | 85 | return domNode; 86 | }; 87 | 88 | /** 89 | * Style 90 | * @param {string|Array} styles 91 | * @param options {{fallback: Function}} 92 | * @return {Function} 93 | */ 94 | function style(styles, options = {}) { 95 | 96 | return Class => { 97 | 98 | initCoreMap(storage, Class); 99 | initStyleMap(storage, Class); 100 | 101 | let map = storage.get(Class); 102 | let styleMap = map.get('@style'); 103 | 104 | // Stylesheets holds the styles (ast) that 105 | // are passed by the @style decorator 106 | styleMap.set('stylesheets', styles); 107 | 108 | // referenceNodes holds the current created domNodes (customElements) 109 | let nodes = styleMap.get('referenceNodes'); 110 | 111 | // stylesheetStates holds the event state (event reached or not) 112 | // of each style-ast in styles object 113 | let stylesheetStates = styleMap.get('stylesheetStates'); 114 | 115 | /** 116 | * Its required when we use @style multiple times! 117 | * Only once registration! 118 | */ 119 | if(map.get('@style').get('callbacksDefined')){ 120 | return; 121 | } 122 | 123 | let createdAndAttached = domNode => { 124 | setPath(domNode, '$stylesheets', []); 125 | // create stylesheet node when no referenceNodes (customelement) exists 126 | if(!nodes.size) { 127 | // default state of each style-ast is false (undefined) 128 | let stylesheetStates = styleMap.get('stylesheetStates'); 129 | createStylesheet(Class, styles, domNode, options, stylesheetStates); 130 | } 131 | !nodes.has(domNode) && nodes.set(domNode, domNode); 132 | }; 133 | 134 | map.get('@callbacks').get('created' ).push(createdAndAttached); 135 | map.get('@callbacks').get('attached').push(createdAndAttached); 136 | map.get('@callbacks').get('detached').push(domNode => { 137 | 138 | // cleanup 139 | domNode.$stylesheets.forEach(stylesheet => stylesheet.destroy()); 140 | 141 | // remove detached node from referenceNodes 142 | nodes.has(domNode) && nodes.delete(domNode); 143 | 144 | // not each domNode has $stylesheets, so return when no once found. 145 | if(!domNode.$stylesheets.length) { 146 | return null; 147 | } 148 | 149 | // stylesheetStates (attachOn) are the event state (reached or not reached) of each 150 | // array object (styles, imports, ...) that are passed by style decorator. 151 | // The state of an array object is required ... 152 | // [ 153 | // { 154 | // attachOn: 'load', 155 | // type: 'on', 156 | // imports: ["a.css", "b.css", "c.css"], 157 | // styles: '.baz {color: green;}', 158 | // }, 159 | // { 160 | // attachOn: 'immediately', 161 | // type: 'lala', 162 | // imports: [], 163 | // styles: '.laz {color: green;}', 164 | // } 165 | // ] 166 | stylesheetStates = syncStylesheetStates(domNode.$stylesheets, stylesheetStates) || []; 167 | 168 | // when elements detached from document.body, we need a new node that take the role of $stylesheet 169 | let node = (nodes.values()).next().value; 170 | 171 | // when node is valid, create fresh stylesheet 172 | if(node) { 173 | createStylesheet(Class, styles, node, options, stylesheetStates); 174 | } 175 | 176 | // save the stylesheetStates 177 | styleMap.set('stylesheetStates', stylesheetStates); 178 | //cleanup 179 | delete domNode.$stylesheets; 180 | 181 | }); 182 | 183 | map.get('@style').set('callbacksDefined', true); 184 | } 185 | } 186 | 187 | export { 188 | style 189 | }; 190 | -------------------------------------------------------------------------------- /src/decorators/view.js: -------------------------------------------------------------------------------- 1 | import { View } from '../libs/view'; 2 | import { extractDomProperties } from '../helpers/extract-dom-properties'; 3 | import { initCoreMap, initViewMap } from '../datas/init-maps'; 4 | import { storage } from '../libs/random-storage'; 5 | import { HandlebarsRuntime } from '../libs/dependencies'; 6 | 7 | /***************************************** 8 | * ######### Public Decorators ########### 9 | *****************************************/ 10 | 11 | /** 12 | * Register View 13 | * @param {String} template 14 | * @param {Object} options 15 | * @return {Function} 16 | */ 17 | function view(template, options = {}) { 18 | 19 | if(!template) { 20 | throw new Exception('Please pass a template!'); 21 | } 22 | 23 | return Class => { 24 | 25 | let renderedFlag = !(options.renderedFlag === false); 26 | 27 | initCoreMap(storage, Class); 28 | initViewMap(storage, Class); 29 | 30 | let map = storage.get(Class); 31 | 32 | /** 33 | * Its required when we use @style multiple times! 34 | * Only once registration! 35 | */ 36 | if(map.get('@view').get('callbacksDefined')){ 37 | return; 38 | } 39 | 40 | map.get('@callbacks').get('created').push((domNode, createVars = {}) => { 41 | 42 | // get and merge dom view var attributes 43 | let domViewAttributes = extractDomProperties( 44 | domNode, /^@view\.bind\.(\S+)$/i, true 45 | ); 46 | 47 | // get the restof regular attributes 48 | let regularDomAttributes = extractDomProperties(domNode); 49 | 50 | let viewBinds = {}; 51 | let entries = map.get('@view').get('bind'); 52 | entries.forEach(([key, value]) => viewBinds[key] = value); 53 | 54 | let viewVars = Object.assign({}, 55 | viewBinds, 56 | domViewAttributes, 57 | createVars, 58 | regularDomAttributes 59 | ); 60 | 61 | // initialize view 62 | let $view = new View({ 63 | prerenderer: HandlebarsRuntime.template, 64 | rootNode: domNode, 65 | vars: viewVars, 66 | template: template, 67 | }); 68 | 69 | view.helper.registerSetGet( 70 | domNode, 71 | Object.assign({}, viewBinds, domViewAttributes) 72 | ); 73 | 74 | // render view 75 | $view.render(null, { 76 | renderedFlag: renderedFlag, 77 | }); 78 | 79 | domNode.$view = $view; 80 | }); 81 | 82 | map.get('@callbacks').get('attached').push(domNode => { 83 | 84 | if(domNode.$view.initialized()){ 85 | return; 86 | } 87 | 88 | /** 89 | * Using the same instance is 30% faster in 90 | * (Chrome, Opera) and no difference in Firefox 91 | * @see: https://jsperf.com/new-class-vs-singleton 92 | */ 93 | domNode.$view.reinit({ 94 | rootNode: domNode, 95 | prerenderer: HandlebarsRuntime.template, 96 | }); 97 | }); 98 | 99 | map.get('@callbacks').get('detached').push(domNode => { 100 | domNode.$view.destroy(); 101 | }); 102 | 103 | map.get('@view').set('callbacksDefined', true); 104 | } 105 | } 106 | 107 | /** 108 | * Register view vars 109 | * @param {Object} target 110 | * @param {String} property 111 | * @param {Object} descriptor 112 | * @return {undefined} 113 | */ 114 | view.bind = (target, property, descriptor) => { 115 | 116 | let Class = target.constructor; 117 | 118 | initCoreMap(storage, Class); 119 | initViewMap(storage, Class); 120 | 121 | // get default value 122 | let value = descriptor.initializer ? descriptor.initializer() : ''; 123 | 124 | // register view binds 125 | let map = storage.get(Class); 126 | map.get('@view').get('bind').push([property, value]); 127 | 128 | }; 129 | 130 | /***************************************** 131 | * ########## Decorator Helper ########### 132 | *****************************************/ 133 | 134 | view.helper = { 135 | 136 | /** 137 | * registerSetGet 138 | * @param target 139 | * @param viewBinds 140 | * @returns {Object} target 141 | */ 142 | registerSetGet(target, viewBinds) { 143 | 144 | // make viewBinds immutable 145 | viewBinds = JSON.parse(JSON.stringify(viewBinds)); 146 | 147 | // prepare property proxy setter 148 | let properties = {}; 149 | for(let property in viewBinds){ 150 | properties[property] = { 151 | set: function(newValue){ 152 | this.$view.set(property, newValue); 153 | this.$view.render(null, {force: true}); 154 | }, 155 | get: function(){ 156 | return this.$view.get(property); 157 | } 158 | } 159 | } 160 | 161 | // prepare render proxy to $.view 162 | properties.render = { 163 | value: function(...args) { 164 | this.$view.set(...args); 165 | this.$view.render(null, {force: true}); 166 | } 167 | }; 168 | 169 | 170 | Object.defineProperties(target, properties); 171 | 172 | return target; 173 | 174 | }, 175 | 176 | }; 177 | 178 | export { 179 | view 180 | }; 181 | -------------------------------------------------------------------------------- /src/helpers/browser-detect.js: -------------------------------------------------------------------------------- 1 | 2 | let isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; 3 | let isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; 4 | let isOpera = (navigator.userAgent.match(/Opera|OPR\//) ? true : false); 5 | 6 | export { 7 | isSafari, 8 | isFirefox, 9 | isOpera, 10 | }; 11 | -------------------------------------------------------------------------------- /src/helpers/delay.js: -------------------------------------------------------------------------------- 1 | let delay = ms => new Promise(resolve => 2 | setTimeout(resolve, ms) 3 | ); 4 | 5 | export { delay }; -------------------------------------------------------------------------------- /src/helpers/extract-dom-properties.js: -------------------------------------------------------------------------------- 1 | let classof = value => { 2 | return Object.prototype.toString.call(value).slice(8, -1); 3 | }; 4 | 5 | /** 6 | * Extract domnode attributes 7 | * @param {HTMLElement} domNode 8 | * @param {Regex} expression 9 | * @param {Boolean} removeDomAttributes 10 | * @return {Object} 11 | */ 12 | function extractDomProperties(domNode, regex, removeDomAttributes = false) { 13 | 14 | 15 | if(regex && classof(regex) !== 'RegExp'){ 16 | throw Error('Second argument is passed but it must be a Regular-Expression'); 17 | } 18 | 19 | let domViewAttributes = {}; 20 | let toBeRemovedAttributes = []; 21 | 22 | for(let key in domNode.attributes) { 23 | if(!domNode.attributes.hasOwnProperty(key)){ 24 | continue; 25 | } 26 | 27 | let node = domNode.attributes[key]; 28 | if(regex){ 29 | let matched = regex.exec(node.name); 30 | if(matched !== null) { 31 | let [ ,name ] = matched; 32 | domViewAttributes[name] = node.value; 33 | removeDomAttributes ? toBeRemovedAttributes.push(node.name) : null; 34 | } 35 | } 36 | else if(!regex) { 37 | domViewAttributes[node.name] = node.value; 38 | } 39 | 40 | } 41 | 42 | if(removeDomAttributes){ 43 | for(let attribute of toBeRemovedAttributes){ 44 | domNode.removeAttribute(attribute); 45 | } 46 | } 47 | 48 | return domViewAttributes; 49 | 50 | } 51 | 52 | export { 53 | extractDomProperties 54 | }; 55 | -------------------------------------------------------------------------------- /src/helpers/guid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate unique id 3 | * @source http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript#105074 4 | * @returns {string} 5 | */ 6 | function guid() { 7 | function _s4() { 8 | return Math.floor( 9 | (1 + Math.random()) * 0x10000 10 | ).toString(16).substring(1); 11 | } 12 | return _s4() + _s4() + '-' + _s4() + '-' + _s4() + '-' + _s4() + '-' + _s4() + _s4() + _s4(); 13 | } 14 | 15 | export { 16 | guid, 17 | }; -------------------------------------------------------------------------------- /src/helpers/history.back-forward-and-wait.js: -------------------------------------------------------------------------------- 1 | import { delay } from './delay'; 2 | 3 | history.backAndWait = ms => { 4 | history.back(); 5 | return delay(ms); 6 | }; 7 | 8 | history.forwardAndWait = ms => { 9 | history.forward(); 10 | return delay(ms); 11 | }; 12 | -------------------------------------------------------------------------------- /src/helpers/jquery.click-and-wait.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { delay } from './delay'; 3 | 4 | $.fn.clickAndWait = function(ms) { 5 | this.get(0).click(); 6 | return delay(ms); 7 | }; -------------------------------------------------------------------------------- /src/helpers/lazyPolyfillLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * promises 3 | * @type {null|Promise} 4 | */ 5 | let promises = null; 6 | 7 | /** 8 | * lazyPolyfillLoader 9 | * @returns {Promise.<*>} 10 | */ 11 | let lazyPolyfillLoader = function(Loader, modules, basePath = '') { 12 | 13 | // when already resolved 14 | if(promises){ 15 | return promises; 16 | } 17 | 18 | // determine which polyfills needed 19 | let _modules = modules.filter(([global, property]) => 20 | !global[property] 21 | ); 22 | 23 | // when nothing to do 24 | if(!_modules.length){ 25 | return Promise.resolve([]); 26 | } 27 | 28 | let _basePath = basePath ? `${basePath}/`: basePath; 29 | 30 | // resolve polyfills 31 | promises = Promise.all(_modules.map(([global, property, path, assign]) => 32 | Loader.import(`${_basePath}${path}`).then(module => 33 | !assign ? global[property] = module : null 34 | ) 35 | )); 36 | 37 | promises.catch((error) => { 38 | throw new Error(error); 39 | }); 40 | 41 | return promises; 42 | }; 43 | 44 | export { 45 | lazyPolyfillLoader 46 | } -------------------------------------------------------------------------------- /src/helpers/namespace.js: -------------------------------------------------------------------------------- 1 | 2 | let namespace = { 3 | 4 | /** 5 | * create a namespace on passed target 6 | * @param {object} target 7 | * @param {string} namespace_string 8 | * @return {target} 9 | */ 10 | create: (target = {}, namespace_string = '', add = null) => { 11 | 12 | let parts = namespace_string.split('.'); 13 | let parent = target; 14 | 15 | for(let i = 0, length = parts.length; i < length; i++) { 16 | 17 | if(typeof parent[parts[i]] === 'undefined') { 18 | parent[parts[i]] = {}; 19 | } 20 | if(add && (i+1) === length) { 21 | parent[parts[i]] = add; 22 | } 23 | parent = parent[parts[i]]; 24 | 25 | } 26 | 27 | return target; 28 | 29 | }, 30 | 31 | }; 32 | 33 | export { 34 | namespace 35 | }; 36 | -------------------------------------------------------------------------------- /src/helpers/queryString.js: -------------------------------------------------------------------------------- 1 | /** 2 | * queryString 3 | * @author Serkan Sipahi 4 | */ 5 | let queryString = { 6 | 7 | /** 8 | * parse 9 | * @param {string} query 10 | * @returns {object} queryObject 11 | */ 12 | parse(query = '') { 13 | 14 | // cleanup 15 | query = query.trim().replace(/^(\?|#|&)/g, ''); 16 | 17 | if(!query || query === ' '){ 18 | return {}; 19 | } 20 | 21 | // init 22 | let searchParams = new URLSearchParams(decodeURIComponent(query)); 23 | let queryObject = Object.create(null); 24 | 25 | // build 26 | for(let param of searchParams.entries()){ 27 | 28 | let key = param[0]; 29 | let value = param[1] || null; 30 | let propertyValue = queryObject[key]; 31 | 32 | // foo=bar&key=val, foo&key 33 | if(propertyValue === undefined && propertyValue !== null){ 34 | queryObject[key] = value; 35 | } 36 | // foo=bar&foo=baz 37 | else { 38 | if(this.classof(propertyValue) !== 'Array'){ 39 | queryObject[key] = [ propertyValue ]; 40 | } 41 | queryObject[key].push(value); 42 | } 43 | } 44 | 45 | return queryObject; 46 | }, 47 | 48 | /** 49 | * stringify 50 | * @param queryObject 51 | * @param encode 52 | * @returns {string} queryString 53 | */ 54 | stringify(queryObject = {}, encode = true) { 55 | 56 | // '', 0, 1, [], etc 57 | if(this.classof(queryObject) !== 'Object'){ 58 | return ''; 59 | } 60 | 61 | // {} 62 | let queryObjectKeys = Object.keys(queryObject); 63 | if(!queryObjectKeys.length){ 64 | return ''; 65 | } 66 | 67 | let raw_query = []; 68 | queryObjectKeys.forEach(key => { 69 | 70 | if(this.classof(queryObject[key]) === 'Array'){ 71 | 72 | queryObject[key].forEach(propValue => { 73 | // { bar: [undefined, 'baz'] } 74 | if(propValue === undefined) return; 75 | // bar: [null, 'baz'] 76 | if(propValue === null){ 77 | raw_query.push(key); 78 | } 79 | // { foo: ['bar', 'baz'] } 80 | else { 81 | raw_query.push(`${key}=${propValue}`) 82 | } 83 | }); 84 | return; 85 | 86 | } 87 | // { abc: undefined } 88 | if(queryObject[key] === undefined) return; 89 | 90 | // { 'def': null } 91 | if(queryObject[key] === null){ 92 | raw_query.push(key); 93 | } 94 | // { foo: 'baz' } 95 | else { 96 | raw_query.push(`${key}=${queryObject[key]}`); 97 | } 98 | 99 | }); 100 | 101 | let queryString = raw_query.join('&'); 102 | 103 | if(encode){ 104 | let { search } = new URL(`http://localhost?${queryString}`); 105 | queryString = search.replace(/^\?/g, ''); 106 | } 107 | 108 | return queryString; 109 | 110 | }, 111 | 112 | classof(value){ 113 | return Object.prototype.toString.call(value).slice(8, -1); 114 | } 115 | 116 | }; 117 | 118 | export { 119 | queryString 120 | }; 121 | -------------------------------------------------------------------------------- /src/helpers/string.js: -------------------------------------------------------------------------------- 1 | 2 | let removeGutter = String.prototype.removeGutter = function(){ 3 | return this.replace(/[\t\n\r]/gm, ''); 4 | }; 5 | 6 | export { 7 | removeGutter, 8 | }; 9 | -------------------------------------------------------------------------------- /src/helpers/trigger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * trigger 3 | * @param {HTMLElement} this 4 | * @param {string} eventType 5 | * @return {undefined} 6 | */ 7 | function trigger(eventType, ...args) { 8 | 9 | let event = new Event(event); 10 | args.length ? event[eventType] = args : null; 11 | this.dispatchEvent(event); 12 | } 13 | 14 | export { 15 | trigger, 16 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // Bootstrap 2 | import './bootstrap'; 3 | import elementToFunction from './libs/element-to-function' 4 | 5 | // Libraries 6 | export { Register } from './libs/customelement'; 7 | export { Eventhandler } from './libs/eventhandler'; 8 | export { View } from './libs/view'; 9 | export { Stylesheet } from './libs/stylesheet'; 10 | export { Router } from './apps/router'; 11 | export { Pubsub } from './apps/pubsub'; 12 | export { storage } from './libs/random-storage'; 13 | export default elementToFunction; 14 | 15 | // Decorators 16 | export { component } from './decorators/component'; 17 | export { view } from './decorators/view'; 18 | export { style } from './decorators/style'; 19 | export { on } from './decorators/on'; 20 | export { action } from './decorators/action'; 21 | export { model } from './decorators/model'; 22 | export { modelpool } from './decorators/modelpool'; -------------------------------------------------------------------------------- /src/libs/customelement.js: -------------------------------------------------------------------------------- 1 | let Register = { 2 | 3 | /** 4 | * Prefix of component if used 5 | * e.g. com-foo 6 | */ 7 | prefix: 'com', 8 | 9 | /** 10 | * customElement 11 | * @param Class {function} 12 | * @param config {{extends: string, name: string}} 13 | */ 14 | customElement(Class, config = {}){ 15 | 16 | let name = config.name; 17 | if(!name){ 18 | let className = this._getClassName(Class); 19 | name = this._createComponentName(className, this.prefix); 20 | } else { 21 | delete config.name; 22 | } 23 | 24 | this._addExtends(Class, config); 25 | this._registerElement(Class, name); 26 | }, 27 | 28 | /** 29 | * _classof 30 | * @param value {*} 31 | * @returns {string} 32 | * @private 33 | */ 34 | _classof(value){ 35 | return Object.prototype.toString.call(value).slice(8, -1); 36 | }, 37 | 38 | /** 39 | * _addExtends 40 | * @param Class {function} 41 | * @param config {{extends: string, name: string}} 42 | * @returns Class {function} 43 | */ 44 | _addExtends(Class, config = {}){ 45 | 46 | let inherit = config.extends; 47 | if(inherit && !Class.extends){ 48 | Class.extends = inherit; 49 | } 50 | 51 | return Class; 52 | }, 53 | 54 | /** 55 | * 56 | * @param Class {function} 57 | * @returns className {string}ls l 58 | */ 59 | _getClassName(Class){ 60 | 61 | return Class.$$componentName || Class.prototype.constructor.name; 62 | }, 63 | 64 | /** 65 | * 66 | * @param className {string} 67 | * @param prefix {string} 68 | * @returns componentName {string} 69 | */ 70 | _createComponentName(className, prefix = this.prefix){ 71 | 72 | return `${prefix}-${className.toLowerCase()}`; 73 | }, 74 | 75 | /** 76 | * _enableConstructorVars 77 | * @param Class {function} 78 | * @private 79 | */ 80 | _enableConstructorVars(Class){ 81 | 82 | let createdCallback = Class.prototype.createdCallback; 83 | Class.prototype.createdCallback = function(...args){ 84 | 85 | if(!args.length && !this.parentElement){ 86 | return; 87 | } 88 | createdCallback ? this::createdCallback(...args) : null; 89 | }; 90 | 91 | return Class; 92 | }, 93 | 94 | /** 95 | * registerElement 96 | * @param Class {function} 97 | * @param name {string} 98 | * @returns Class {function} 99 | */ 100 | _registerElement(Class, name){ 101 | 102 | this._enableConstructorVars(Class); 103 | 104 | // register element 105 | let registeredElement = document.registerElement(name, Class); 106 | 107 | /** 108 | * create (add factory) 109 | * @param vars {object} 110 | */ 111 | Class.create = function(vars) { 112 | 113 | if(arguments.length > 1){ 114 | throw new Error('Its not allowed to pass more than one argument'); 115 | } 116 | 117 | let classof = Register._classof(vars); 118 | if(!(classof === 'Object' || classof === 'Undefined')) { 119 | throw new Error('Passed argument must be an object or undefined'); 120 | } 121 | 122 | let element = new registeredElement(); 123 | 124 | element.createdCallback(vars || ""); 125 | return element; 126 | }; 127 | 128 | return Class; 129 | } 130 | }; 131 | 132 | export { 133 | Register, 134 | }; 135 | -------------------------------------------------------------------------------- /src/libs/dependencies.js: -------------------------------------------------------------------------------- 1 | export { default as HandlebarsRuntime } from '../../node_modules/handlebars/dist/handlebars.runtime'; 2 | export { default as RegExp } from '../../node_modules/named-js-regexp/lib/named-js-regexp'; 3 | export { default as extend } from '../../node_modules/extend/index'; -------------------------------------------------------------------------------- /src/libs/element-to-function.js: -------------------------------------------------------------------------------- 1 | let elementToFunction = Element => { 2 | if(typeof Element === 'function'){ 3 | return Element; 4 | } 5 | 6 | let _Element = function(){}; 7 | _Element.prototype = Element.prototype; 8 | return _Element; 9 | }; 10 | 11 | export default elementToFunction; -------------------------------------------------------------------------------- /src/libs/pubsub.js: -------------------------------------------------------------------------------- 1 | 2 | class Pubsub { 3 | 4 | scope = {}; 5 | 6 | constructor(config){ 7 | 8 | Object.assign(this, config); 9 | } 10 | 11 | publish(event, ...args){ 12 | 13 | this.scope.trigger(event, ...args); 14 | } 15 | 16 | subscribe(event, callback){ 17 | 18 | this.scope.on(event, callback) 19 | } 20 | 21 | unsubscribe(event){ 22 | 23 | this.scope.off(event); 24 | } 25 | 26 | } 27 | 28 | export { 29 | Pubsub 30 | }; 31 | -------------------------------------------------------------------------------- /src/libs/random-storage.js: -------------------------------------------------------------------------------- 1 | let storage = new Map(); 2 | 3 | export { 4 | storage, 5 | }; -------------------------------------------------------------------------------- /src/libs/router.runtime.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerkanSipahi/app-decorators/15f046b1bbe56af0d45b4a1b45a6a4c5689e4763/src/libs/router.runtime.js -------------------------------------------------------------------------------- /test/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-async-to-generator" 4 | ], 5 | "presets": [ 6 | ["es2015", { 7 | "modules" : "systemjs" 8 | }], 9 | "app-decorators" 10 | ] 11 | } -------------------------------------------------------------------------------- /test/decorators/component.spec.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { bootstrapPolyfills } from '../../src/bootstrap'; 3 | import { delay } from '../../src/helpers/delay'; 4 | 5 | describe('@component', async () => { 6 | 7 | await bootstrapPolyfills; 8 | let { component, getComponentName } = await System.import('src/decorators/component'); 9 | 10 | describe('getComponentName', () => { 11 | 12 | it('should return name', () => { 13 | 14 | let element1 = document.createElement('com-foo'); 15 | element1::getComponentName().should.be.equal('com-foo'); 16 | 17 | let element2 = document.createElement('img'); 18 | element2.setAttribute('is', 'my-image'); 19 | element2::getComponentName().should.be.equal('img[is="my-image"]'); 20 | 21 | }); 22 | 23 | }); 24 | 25 | describe('decorator', () => { 26 | 27 | @component() 28 | class Foo {} 29 | 30 | it('should instance of HTMLElement', () => { 31 | let foo = Foo.create(); 32 | foo.should.be.instanceOf(HTMLElement); 33 | foo.outerHTML.should.equal(''); 34 | }); 35 | 36 | // *********************** // 37 | 38 | @component({ 39 | extends: 'img' 40 | }) 41 | class Bar { 42 | created() { 43 | this.src = 'path/to/picture.png'; 44 | } 45 | } 46 | 47 | it('should instance of HTMLImageElement', () => { 48 | let bar = Bar.create(); 49 | bar.should.be.instanceOf(HTMLImageElement); 50 | 51 | try { 52 | bar.outerHTML.should.equal(''); 53 | } catch(e){ 54 | bar.outerHTML.should.equal(''); 55 | } 56 | }); 57 | 58 | // *********************** // 59 | 60 | @component({ 61 | name: 'my-bier' 62 | }) 63 | class Baz {} 64 | 65 | it('should instance of HTMLElement + custom componentname', () => { 66 | let baz = Baz.create(); 67 | baz.should.be.instanceOf(HTMLElement); 68 | baz.outerHTML.should.equal(''); 69 | }); 70 | 71 | // *********************** // 72 | 73 | @component({ 74 | name: 'my-qux', 75 | extends: 'form', 76 | }) 77 | class Qux { 78 | created(){ 79 | this.innerHTML = 'Hello World!'; 80 | } 81 | } 82 | 83 | it('should instance of HTMLFormElement + custom componentname', () => { 84 | let qux = Qux.create(); 85 | qux.should.be.instanceOf(HTMLFormElement); 86 | qux.outerHTML.should.equal('
    Hello World!
    '); 87 | }); 88 | 89 | it('should create a element by passed @component config (name, extends) from out of dom', async () => { 90 | 91 | $('body').append('
    '); 92 | 93 | await delay(1); 94 | 95 | // test 96 | $('form[is="my-qux"]').get(0).outerHTML.should.equal('
    Hello World!
    '); 97 | //cleanup 98 | $('form[is="my-qux"]').remove(); 99 | 100 | }); 101 | }); 102 | 103 | }); 104 | -------------------------------------------------------------------------------- /test/decorators/pipe.spec.js: -------------------------------------------------------------------------------- 1 | describe.skip('@pipe decorator', () => { 2 | 3 | // 1.) alle componenten inhalte instance(domNode) als blob speichern um sie beim nächsten mal als local file zu laden. 4 | // Idee: alle componenten können miteinander kommunizieren, wenn sie 5 | // mit x-foo oder mit irgendeinem html tag z.B.
    6 | // erstellt wurden sind. 7 | // 8 | // 2.) Man verringert den komplexität wert 9 | 10 | it('...', () => { 11 | 12 | @error({ level: 3 }) 13 | @datapool('information') 14 | 15 | @style(` 16 | x-stream { 17 | 18 | $deltaX: @on('wheel').currentTarget.deltaX; 19 | $deltaY: @on('wheel').currentTarget.deltaY; 20 | 21 | top: $deltaX; 22 | left: $deltaY; 23 | position: 'absolute'; 24 | 25 | content: "{{ Click Me! 35 | 36 | 37 | 38 | 39 | 40 | 41 | {{ 42 | 43 | `) 44 | */ 45 | @component({ 46 | name: 'x-stream' 47 | }) 48 | 49 | class Stream { 50 | 51 | created(){ 52 | 53 | this.model.vorname = 'Serkan'; 54 | this.model.nachname = 'Sipahi'; 55 | } 56 | 57 | attached(){ 58 | 59 | this.trigger('startCircleAnimation'); 60 | } 61 | 62 | @on('wheel') @debounce(100) onWheel(event){ 63 | 64 | console.log(event.deltaX, event.deltaY); 65 | } 66 | 67 | /** 68 | * Model settings 69 | */ 70 | @model.attr vorname = ''; 71 | @model.attr nachname = ''; 72 | 73 | @model.attr links = []; 74 | 75 | @model('change:vorname') onChangeVorname(oldValue, newValue){ 76 | 77 | this.view.vorname = newValue; 78 | } 79 | @model('change:nachname') onChangeNachname(oldValue, newValue){ 80 | 81 | this.view.nachname = newValue; 82 | } 83 | 84 | @on('get-links') 85 | @$if @cond(this.request.url) 86 | @request(this.request.url) 87 | @$else 88 | @request('http://bing.com') 89 | @extractFromString(//gm) 90 | 91 | onExtractedHTML(links, error) { 92 | 93 | if(error){ 94 | throw this.Error('Es ist ein Fehler aufgereten!'); 95 | } 96 | 97 | this.model.links.push(links); 98 | 99 | console.log(this.informationen.model.title); 100 | } 101 | 102 | @model('change:links') onChangeLinks(oldValue, newValue) { 103 | 104 | this.view.links = newValue; 105 | } 106 | 107 | /** 108 | * on 109 | * @param {string} 'x-button' 110 | * @return {undefined} 111 | */ 112 | 113 | @view.bind id = 0; 114 | 115 | @on('startCircleAnimation') 116 | 117 | @pipe(animationConfig('ease-in-out')) 118 | @pipe(moveup('100px', '1s'), 'async') 119 | @pipe(moveleft('200px', '500ms'), 'async') 120 | @pipe(movedown('200px', '800ms'), 'sync') 121 | @pipe(moveright('200px', '1s'), 'sync') 122 | 123 | animation_MoveCircleInBox(result, error){ 124 | 125 | if(error){ 126 | throw this.Error('Es ist ein Fehler aufgereten!'); 127 | } 128 | 129 | console.log('animation done!'); 130 | } 131 | 132 | onClickButton(event){ 133 | 134 | this.querySelector('a[is="x-a"]').click(); 135 | } 136 | 137 | @on('get', '?stream&firstname={{name}}&lastname{{nachname}}') onPost({name, nachname}){ 138 | 139 | this.model.vorname = name; 140 | this.model.nachname = nachname; 141 | } 142 | 143 | } 144 | }); 145 | }); 146 | -------------------------------------------------------------------------------- /test/fixture/server-styles-4000.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let app = express(); 3 | 4 | app.use(express.static('./test/fixture')); 5 | app.listen(4000, () => console.log(`Server: localhost for styles: ${4000}`)); -------------------------------------------------------------------------------- /test/fixture/styles/test-1.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | width: 100px; 3 | background-color: rgb(001, 010, 100); 4 | } -------------------------------------------------------------------------------- /test/fixture/styles/test-2.css: -------------------------------------------------------------------------------- 1 | .bar { 2 | width: 200px; 3 | background-color: rgb(002, 020, 200); 4 | } -------------------------------------------------------------------------------- /test/fixture/styles/test-3.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | width: 300px; 3 | background-color: rgb(100, 200, 255); 4 | } -------------------------------------------------------------------------------- /test/helpers/extract-dom-properties.spec.js: -------------------------------------------------------------------------------- 1 | import { extractDomProperties } from '../../src/helpers/extract-dom-properties'; 2 | 3 | describe('extractDecoratorProperties', () => { 4 | 5 | function MockElement(attributes){ 6 | this.attributes = attributes; 7 | } 8 | 9 | MockElement.prototype = { 10 | // simulate removing of removeAttribute 11 | removeAttribute : function(attribute) { 12 | for(let attr in this.attributes){ 13 | if(!this.attributes.hasOwnProperty(attr)) continue; 14 | if(this.attributes[attr].name === attribute){ 15 | delete this.attributes[attr]; 16 | } 17 | } 18 | } 19 | }; 20 | 21 | it('it should return objectlist of dom attributes that matched of @view.bind', () => { 22 | 23 | let MockDomNode = new MockElement({ 24 | 0: { name: '@view.bind.b', value: 'baz' }, 25 | 1: { name: 'class', value: 'foo' }, 26 | 2: { name: '@view.bind.x', value: 'bar' }, 27 | }); 28 | 29 | let viewAttributeList = extractDomProperties(MockDomNode, /^@view\.bind\.(\S+)$/i, true); 30 | 31 | // positiv test 32 | viewAttributeList.should.have.propertyByPath('x').eql('bar'); 33 | viewAttributeList.should.have.propertyByPath('b').eql('baz'); 34 | // negativ test 35 | viewAttributeList.should.not.have.propertyByPath('class'); 36 | 37 | // the third argument for getDomAttributes removes the dom attribute 38 | Object.keys(MockDomNode.attributes).should.have.length(1); 39 | MockDomNode.attributes.should.have.propertyByPath(1, 'name').eql('class'); 40 | 41 | let regularAttributeList = extractDomProperties(MockDomNode); 42 | regularAttributeList.should.have.propertyByPath('class').eql('foo'); 43 | 44 | }); 45 | 46 | it('it should return objectlist of dom attributes that matched of @on', () => { 47 | 48 | let MockDomNode = new MockElement({ 49 | 0: { name: '@on(click)', value: 'clickFunction()' }, 50 | 1: { name: 'id', value: 'bazi' }, 51 | 2: { name: '@on(mousedown)', value: 'mousedownFunction()' }, 52 | }); 53 | 54 | let viewAttributeList = extractDomProperties(MockDomNode, /^@on\((\S+)\)$/i, true); 55 | 56 | // positiv test 57 | viewAttributeList.should.have.propertyByPath('click').eql('clickFunction()'); 58 | viewAttributeList.should.have.propertyByPath('mousedown').eql('mousedownFunction()'); 59 | // negativ test 60 | viewAttributeList.should.not.have.propertyByPath('id'); 61 | 62 | // the third argument for getDomAttributes removes the dom attribute 63 | Object.keys(MockDomNode.attributes).should.have.length(1); 64 | MockDomNode.attributes.should.have.propertyByPath(1, 'name').eql('id'); 65 | 66 | let regularAttributeList = extractDomProperties(MockDomNode); 67 | regularAttributeList.should.have.propertyByPath('id').eql('bazi'); 68 | 69 | }); 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /test/helpers/guid.spec.js: -------------------------------------------------------------------------------- 1 | import { guid } from '../../src/helpers/guid'; 2 | 3 | describe('guid', () => { 4 | 5 | it('should generate uid', () => { 6 | 7 | // test 8 | let uid1 = guid(); 9 | let uid2 = guid(); 10 | 11 | uid1.should.match(/^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+$/); 12 | uid1.should.not.be.equal(uid2); 13 | 14 | }); 15 | 16 | }); -------------------------------------------------------------------------------- /test/helpers/namespace.spec.js: -------------------------------------------------------------------------------- 1 | import { namespace } from '../../src/helpers/namespace'; 2 | 3 | describe('namespace', () => { 4 | 5 | describe('create method', () => { 6 | 7 | it('should create namespace object by passed target and namespace_string', () => { 8 | 9 | let target = {}; 10 | target = namespace.create(target, 'a.b.c'); 11 | target = namespace.create(target, 'a.b.d'); 12 | 13 | target.should.have.propertyByPath('a', 'b', 'c'); 14 | target.should.have.propertyByPath('a', 'b', 'd'); 15 | 16 | }); 17 | 18 | it('should create namespace object by passed target, namespace_string and add object', () => { 19 | 20 | let target = {}; 21 | target = namespace.create(target, 'a.b.d', 1234); 22 | target = namespace.create(target, 'a.b.c', 5678); 23 | 24 | target.should.have.containEql({a:{b:{c: 5678, d: 1234 }}}); 25 | 26 | }); 27 | 28 | }); 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /test/helpers/querString.spec.js: -------------------------------------------------------------------------------- 1 | import { queryString } from '../../src/helpers/queryString'; 2 | 3 | describe('queryString', () => { 4 | 5 | describe('parse method', () => { 6 | 7 | it('query strings starting with a `?`', () => { 8 | should(queryString.parse('?foo=bar')).be.containEql({foo: 'bar'}); 9 | }); 10 | 11 | it('query strings starting with a `&`', () => { 12 | should(queryString.parse('#foo=bar')).be.containEql({foo: 'bar'}); 13 | }); 14 | 15 | it('query strings starting with a `&`', () => { 16 | should(queryString.parse('&foo=bar&foo=baz')).be.containEql({foo: ['bar', 'baz']}); 17 | }); 18 | 19 | it('parse a query string', () => { 20 | should(queryString.parse('foo=bar')).be.containEql({foo: 'bar'}); 21 | }); 22 | 23 | it('parse multiple query string', () => { 24 | should(queryString.parse('foo=bar&key=val')).be.containEql({ 25 | foo: 'bar', 26 | key: 'val' 27 | }); 28 | }); 29 | 30 | it('parse query string without a value', () => { 31 | should(queryString.parse('foo')).be.containEql({foo: null}); 32 | 33 | should(queryString.parse('foo&key')).be.containEql({ 34 | foo: null, 35 | key: null 36 | }); 37 | 38 | should(queryString.parse('foo=bar&key')).be.containEql({ 39 | foo: 'bar', 40 | key: null 41 | }); 42 | 43 | should(queryString.parse('a&a=')).be.containEql({a: [null, null]}); 44 | }); 45 | 46 | it('return empty object if no qss can be found', () => { 47 | should(queryString.parse('?')).be.containEql({}); 48 | should(queryString.parse('&')).be.containEql({}); 49 | should(queryString.parse('#')).be.containEql({}); 50 | should(queryString.parse('') ).be.containEql({}); 51 | should(queryString.parse(' ')).be.containEql({}); 52 | }); 53 | 54 | it('handle `+` correctly', () => { 55 | should(queryString.parse('foo+faz=bar+baz++')).be.containEql({'foo faz': 'bar baz '}); 56 | }); 57 | 58 | it('handle multiple of the same key', () => { 59 | should(queryString.parse('foo=bar&foo=baz')).be.containEql({foo: ['bar', 'baz']}); 60 | }); 61 | 62 | it('handle multiple of the same key', () => { 63 | should(queryString.parse('foo=bar&foo=baz')).be.containEql({foo: ['bar', 'baz']}); 64 | }); 65 | 66 | it('query strings params including embedded `=`', () => { 67 | should(queryString.parse('?param=http%3A%2F%2Fsomeurl%3Fid%3D2837')).be.containEql({ 68 | param: 'http://someurl?id=2837' 69 | }); 70 | }); 71 | 72 | it('query strings params including raw `=`', () => { 73 | should(queryString.parse('?param=http://someurl?id=2837')).be.containEql({ 74 | param: 'http://someurl?id=2837' 75 | }); 76 | }); 77 | 78 | it('object properties', () => { 79 | should(queryString.parse('hasOwnProperty=foo')).be.containEql({hasOwnProperty: 'foo'}); 80 | }); 81 | 82 | }); 83 | 84 | describe('stringify method', () => { 85 | 86 | it('stringify', () => { 87 | queryString.stringify({foo: 'bar'}).should.be.equal('foo=bar'); 88 | queryString.stringify({foo: 'bar', 'bar': 'baz'}).should.be.equal('foo=bar&bar=baz'); 89 | }); 90 | 91 | 92 | it('different types', () => { 93 | queryString.stringify('').should.be.equal(''); 94 | queryString.stringify(0).should.be.equal(''); 95 | queryString.stringify(1).should.be.equal(''); 96 | queryString.stringify([]).should.be.equal(''); 97 | queryString.stringify(true).should.be.equal(''); 98 | }); 99 | 100 | it('URI encode', () => { 101 | queryString.stringify({'foo bar': 'baz faz'}).should.be.equal('foo%20bar=baz%20faz'); 102 | // FIXME: should be implemented when its required 103 | //queryString.stringify({'foo bar': "baz\'faz"}).should.be.equal('foo%20bar=baz%27faz'); 104 | }); 105 | 106 | it('no encoding', () => { 107 | queryString.stringify({'foo:bar': 'baz:faz'}, false).should.be.equal('foo:bar=baz:faz'); 108 | }); 109 | 110 | it('handle array value', () => { 111 | queryString.stringify({ 112 | abc: 'abc', 113 | foo: ['bar', 'baz'] 114 | }).should.be.equal('abc=abc&foo=bar&foo=baz'); 115 | }); 116 | 117 | it('handle empty array value', () => { 118 | queryString.stringify({ 119 | abc: 'abc', 120 | foo: [] 121 | }).should.be.equal('abc=abc'); 122 | }); 123 | 124 | it('should not encode undefined values', () => { 125 | queryString.stringify({ 126 | abc: undefined, 127 | foo: 'baz' 128 | }).should.be.equal('foo=baz'); 129 | }); 130 | 131 | it('should encode null values as just a key', () => { 132 | queryString.stringify({ 133 | 'x y z': null, 134 | 'abc': null, 135 | 'foo': 'baz' 136 | }).should.be.equal('x%20y%20z&abc&foo=baz'); 137 | }); 138 | 139 | it('handle null values in array', () => { 140 | queryString.stringify({ 141 | foo: null, 142 | bar: [null, 'baz'] 143 | }).should.be.equal('foo&bar&bar=baz'); 144 | }); 145 | 146 | it('handle undefined values in array', () => { 147 | queryString.stringify({ 148 | foo: null, 149 | bar: [undefined, 'baz'] 150 | }).should.be.equal('foo&bar=baz'); 151 | }); 152 | 153 | it('handle undefined and null values in array', () => { 154 | queryString.stringify({ 155 | foo: null, 156 | bar: [undefined, null, 'baz'] 157 | }).should.be.equal('foo&bar&bar=baz'); 158 | }); 159 | 160 | // FIXME: should be implemented when its required 161 | it.skip('strict encoding', () => { 162 | queryString.stringify({foo: '\'bar\''}).should.be.equal('foo=%27bar%27'); 163 | queryString.stringify({foo: ['\'bar\'', '!baz']}).should.be.equal('foo=%27bar%27&foo=!baz'); 164 | }); 165 | 166 | // FIXME: should be implemented when its required 167 | it.skip('loose encoding', () => { 168 | queryString.stringify({foo: '\'bar\''}).should.be.equal('foo=\'bar\''); 169 | queryString.stringify({foo: ['\'bar\'', '!baz']}).should.be.equal('foo=\'bar\''); 170 | }); 171 | 172 | }); 173 | 174 | }); 175 | 176 | -------------------------------------------------------------------------------- /test/helpers/remove-gutter.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerkanSipahi/app-decorators/15f046b1bbe56af0d45b4a1b45a6a4c5689e4763/test/helpers/remove-gutter.js -------------------------------------------------------------------------------- /test/imports/app.spec.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { bootstrapPolyfills } from '../../src/bootstrap'; 3 | import { delay } from '../../src/helpers/delay'; 4 | 5 | describe('imported component', async() => { 6 | 7 | await bootstrapPolyfills; 8 | let { component } = await System.import('app-decorators'); 9 | let { Test } = await System.import('test/imports/testcomponent'); 10 | 11 | @component() 12 | class Col { 13 | 14 | created({ testEl }){ 15 | 16 | this.item = { 17 | name: 'A', 18 | content: 1 19 | }; 20 | 21 | testEl.render(this.item); 22 | $(this).append(testEl); 23 | } 24 | 25 | } 26 | 27 | it('should create an domnode', async () => { 28 | 29 | let col = Col.create({ 30 | testEl: Test.create() 31 | }); 32 | 33 | await delay(5); 34 | col.outerHTML.should.equal(`
    1
    `); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /test/imports/testcomponent.js: -------------------------------------------------------------------------------- 1 | import { component, view } from 'app-decorators'; 2 | 3 | @view(`
    {{content}}
    `, { renderedFlag: false }) 4 | @component() 5 | class Test { 6 | 7 | @view.bind name; 8 | @view.bind content; 9 | 10 | } 11 | 12 | export { 13 | Test 14 | } 15 | -------------------------------------------------------------------------------- /test/libs/customelement.spec.js: -------------------------------------------------------------------------------- 1 | import { bootstrapPolyfills } from '../../src/bootstrap'; 2 | import { delay } from '../../src/helpers/delay'; 3 | 4 | describe('Register customelement', async () => { 5 | 6 | await bootstrapPolyfills; 7 | let { Register } = await System.import('src/libs/customelement'); 8 | 9 | describe('_getClassName method', () => { 10 | 11 | it('should return given class name', () => { 12 | 13 | class Foo {} 14 | Register._getClassName(Foo).should.be.equal('Foo'); 15 | 16 | }); 17 | 18 | }); 19 | 20 | describe('_createComponentName method', () => { 21 | 22 | it('should create component name by given name and prefix', () => { 23 | 24 | Register._createComponentName('Foo', 'com').should.be.equal('com-foo'); 25 | Register._createComponentName('Bar').should.be.equal('com-bar'); 26 | 27 | }); 28 | 29 | }); 30 | 31 | describe('_addExtends method', () => { 32 | 33 | it('should add extends prop by _config', () => { 34 | 35 | class Foo {} 36 | Foo = Register._addExtends(Foo, { extends: 'img' }); 37 | 38 | Foo.should.have.propertyByPath('extends').equal('img'); 39 | 40 | }); 41 | 42 | it('should not add extends if not exists', () => { 43 | 44 | class Foo {} 45 | Foo = Register._addExtends(Foo); 46 | 47 | Foo.should.not.have.propertyByPath('extends'); 48 | 49 | }); 50 | 51 | }); 52 | 53 | describe('_registerElement method', () => { 54 | 55 | it('should add create factory by given Class', () => { 56 | 57 | class MyLang extends HTMLDivElement {} 58 | MyLang = Register._registerElement(MyLang, 'rust-lang'); 59 | 60 | MyLang.should.have.propertyByPath('create'); 61 | 62 | }); 63 | 64 | it('should create instanceof HTMLElement', () => { 65 | 66 | class MyLang extends HTMLDivElement { 67 | createdCallback({ text }){ 68 | this.innerHTML = text; 69 | } 70 | } 71 | MyLang = Register._registerElement(MyLang, 'go-lang'); 72 | 73 | MyLang.create({ text: 'Hello World' }).should.be.instanceOf(HTMLElement); 74 | MyLang.create({ text: 'Hello World' }).outerHTML.should.be.equal('Hello World'); 75 | 76 | }); 77 | 78 | it('should create instanceof HTMLImageElement', () => { 79 | 80 | class MyLang extends HTMLImageElement { 81 | createdCallback({ src }){ 82 | this.src = src; 83 | } 84 | static get extends(){ 85 | return 'img'; 86 | } 87 | } 88 | MyLang = Register._registerElement(MyLang, 'go-image'); 89 | 90 | MyLang.create({}).should.be.instanceOf(HTMLImageElement); 91 | MyLang.create({ src: 'golang.png' }).src.should.match(/golang\.png/); 92 | 93 | }); 94 | 95 | it('should not throw error not arguments passed', () => { 96 | 97 | class MyLang extends HTMLDivElement {} 98 | MyLang = Register._registerElement(MyLang, 'c-lang'); 99 | 100 | (() => { MyLang.create(); }).should.not.throw(); 101 | 102 | }); 103 | 104 | it('should throw error when more than one argument added', () => { 105 | 106 | class MyLang extends HTMLDivElement {} 107 | MyLang = Register._registerElement(MyLang, 'java-lang'); 108 | 109 | (() => MyLang.create({ text: 'Hello World' }, 'just-arg')).should.throw(); 110 | 111 | }); 112 | 113 | it('should throw error when not object or undefined passed as argument', () => { 114 | 115 | class MyLang extends HTMLDivElement {} 116 | MyLang = Register._registerElement(MyLang, 'ruby-lang'); 117 | 118 | (() => MyLang.create('hello world') ).should.throw(); 119 | (() => MyLang.create(true) ).should.throw(); 120 | (() => MyLang.create(1234) ).should.throw(); 121 | 122 | }); 123 | 124 | }); 125 | 126 | describe('customElement method (integration test)', () => { 127 | 128 | it('should create instance of image element', () => { 129 | 130 | class MyImage extends HTMLImageElement { 131 | createdCallback({ src }){ 132 | this.src = src; 133 | } 134 | } 135 | 136 | Register.customElement(MyImage, { 137 | extends: 'img', 138 | name: 'coding-image', 139 | }); 140 | 141 | let myImageElement = MyImage.create({ src: 'golang.png' }); 142 | 143 | myImageElement.should.be.instanceOf(HTMLImageElement); 144 | myImageElement.src.should.match(/golang\.png/); 145 | 146 | }); 147 | 148 | it('should call foo bar baz if created', () => { 149 | 150 | class MyLang extends HTMLDivElement { 151 | createdCallback({ value }){ 152 | this.$value = value; 153 | } 154 | foo() { 155 | return this.bar(); 156 | } 157 | bar() { 158 | return this.$value; 159 | } 160 | } 161 | 162 | Register.customElement(MyLang, { 163 | name: 'python-lang', 164 | }); 165 | let myLang = MyLang.create({ value: 1234 }); 166 | 167 | // setup tests (spying) 168 | sinon.spy(myLang, "foo"); 169 | sinon.spy(myLang, "bar"); 170 | 171 | // start tests 172 | myLang.foo().should.be.equal(1234); 173 | myLang.foo.calledOnce.should.be.true(); 174 | myLang.bar.calledOnce.should.be.true(); 175 | 176 | // cleanup (tearDown) 177 | myLang.foo.restore(); 178 | myLang.bar.restore(); 179 | 180 | }); 181 | 182 | it('should call foo bar baz if created by dom it self', async () => { 183 | 184 | class MyLang extends HTMLFormElement { 185 | createdCallback(){ 186 | this.$value = 1234; 187 | } 188 | foo() { 189 | return this.bar(); 190 | } 191 | bar() { 192 | return this.$value; 193 | } 194 | } 195 | 196 | Register.customElement(MyLang, { 197 | name: 'coffee-lang', 198 | extends: 'form' 199 | }); 200 | 201 | let spy_myLang_foo = sinon.spy(MyLang.prototype, "foo"); 202 | let spy_myLang_bar = sinon.spy(MyLang.prototype, "bar"); 203 | 204 | let div = document.createElement('div'); 205 | div.id = 'customelement-coffee'; 206 | document.body.appendChild(div); 207 | div.innerHTML = '
    '; 208 | 209 | await delay(10); 210 | 211 | // start tests 212 | document.querySelector('[is="coffee-lang"]').foo().should.be.equal(1234); 213 | spy_myLang_foo.calledOnce.should.be.true(); 214 | spy_myLang_bar.calledOnce.should.be.true(); 215 | 216 | // cleanup (tearDown) 217 | spy_myLang_foo.restore(); 218 | spy_myLang_bar.restore(); 219 | 220 | }); 221 | }); 222 | 223 | }); 224 | -------------------------------------------------------------------------------- /test/libs/element-to-function.spec.js: -------------------------------------------------------------------------------- 1 | import elementToFunction from '../../src/libs/element-to-function'; 2 | 3 | describe('elementToFunction', () => { 4 | 5 | it('should return same class when its an Function', () => { 6 | 7 | elementToFunction(HTMLElement).should.have.property('prototype'); 8 | elementToFunction(HTMLElement).should.be.Function(); 9 | elementToFunction(HTMLElement).should.be.equal(HTMLElement); 10 | 11 | }); 12 | 13 | it('should make object to Function with prototype', () => { 14 | 15 | let elementObject = {}; 16 | elementToFunction(elementObject).should.have.property('prototype'); 17 | elementToFunction(elementObject).should.be.Function(); 18 | 19 | }); 20 | 21 | }); -------------------------------------------------------------------------------- /test/mocks/event.js: -------------------------------------------------------------------------------- 1 | 2 | class Event { 3 | 4 | bubbles = false; 5 | cancelBubble = false; 6 | cancelable = false; 7 | composed = false; 8 | currentTarget = null; 9 | defaultPrevented = false; 10 | eventPhase = 0; 11 | isTrusted = false; 12 | path = []; 13 | returnValue = true; 14 | srcElement = null; 15 | target = null; 16 | timeStamp = null; 17 | type = null; 18 | 19 | preventDefault(){} 20 | stopPropagation(){} 21 | 22 | constructor(type, config){ 23 | Object.assign(this, config, { type }); 24 | this._init(); 25 | } 26 | 27 | _init(){ 28 | this._setTimestamp(); 29 | } 30 | 31 | _setTimestamp(){ 32 | this.timeStamp = new Date().getTime(); 33 | } 34 | } 35 | 36 | export { Event }; 37 | -------------------------------------------------------------------------------- /test/mocks/location.js: -------------------------------------------------------------------------------- 1 | 2 | class Location { 3 | 4 | href = false; 5 | 6 | constructor(config){ 7 | Object.assign(this, config); 8 | } 9 | } 10 | 11 | export { Location }; 12 | --------------------------------------------------------------------------------