Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
JooseNavarro committed Mar 2, 2020
0 parents commit 4cefacb
Show file tree
Hide file tree
Showing 19 changed files with 369 additions and 0 deletions.
24 changes: 24 additions & 0 deletions .gitignore
@@ -0,0 +1,24 @@
# compiled output
/dist
/tmp

# dependencies
/node_modules


# IDEs and editors
/.idea
/.vscode
.npmrc


#System Files
.DS_Store

# lock
yarn.lock
package-lock.json

*.log


4 changes: 4 additions & 0 deletions .npmignore
@@ -0,0 +1,4 @@
// core
lib
node_modules

58 changes: 58 additions & 0 deletions README.md
@@ -0,0 +1,58 @@
# Web Adapter
Web Adapter es un paquete que te permite tener una comunicación dinámica con tus Web Componets externos, ademas podrás cargar tus estilos de forma dinámica.

- Facil de utilizar.
- Carga async de tus componentes.
- Puedes Listar y eliminar tus web component.


### Instalación

```sh
$ cd myApp
$ npm install web-adapter-js
```
#
En tu modulo principal agrega esta configuración.
```
import { Adapter } from 'web-adapter-js';
export class AppModule {
constructor() {
const adapter = new Adapter();
adapter.init(['angular']);
}
}
```
### Agregar un Web Component
```
@Component({
selector: 'app-component',
template: `<my-component></my-component>`,
})
export class AppComponent implements OnInit {
public componentAdapter = new ComponentAdapter();
public uiAdapter = new UiAdapter();
constructor() { }
ngOnInit() {
this.uiAdapter.loadStyles([{name: 'my-style', src: 'https://cdn.com/my-style.css'}]);
this.microAdapter.loadComponents([{name: 'my-component', src: 'http://localhost:8000/main.js'}]);
}
}
```


License
----

MIT

----

`Jose Navarro - Trabajo con <3`
8 changes: 8 additions & 0 deletions jest-config.ts
@@ -0,0 +1,8 @@
module.exports = {
roots: ['<rootDir>/node_modules/ts-jest/preprocessor.js'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
}
17 changes: 17 additions & 0 deletions lib/adapters/adapter.ts
@@ -0,0 +1,17 @@
import { AngularConfig, Framework } from "../enum";
import { ComponentAdapter } from "./component/component.adapter";

export class Adapter {

public init( frameworks: Array<string> = [] ): void {
frameworks.forEach( (data: string) => {
if (data === Framework.angular) this.initialAngular('initAngular');
});
}

private initialAngular( name: string ): void {
const scriptElement = new ComponentAdapter();
scriptElement.createScript( name, AngularConfig.zone);
}

}
34 changes: 34 additions & 0 deletions lib/adapters/component/component.adapter.ts
@@ -0,0 +1,34 @@
import { BasicElement, ElementDescription, StatusElement } from "../../interfaces";
import { BehaviorSubject, Observable } from "rxjs";
import { CreateScript } from "./create-script";

export class ComponentAdapter {

public allComponent: Array<BasicElement> = [];
private statusComponent: BehaviorSubject<any> = new BehaviorSubject(null);

public getStatusComponent(): Observable<StatusElement> {
return this.statusComponent.asObservable();
}

public loadComponents(microComponent: Array<ElementDescription> = []) {
microComponent.forEach(({ name, src }) => this.createScript(name, src));
}

public allRemoveComponent(): void {
this.allComponent.forEach(( { element } : BasicElement)=> element.remove());
}

public removeOneComponent(name: string): void {
this.allComponent.filter( (component: BasicElement, index: number) => component.name === name)
.forEach( ( { element } : BasicElement)=> element.remove());
}

public createScript( name: string, src: string): void {
const script = new CreateScript();
script.build( name, src ).then((data: StatusElement) => {
this.statusComponent.next(data);
this.allComponent.push({name: data.name, element: data.element});
}).catch( e => this.statusComponent.next(e));
}
}
11 changes: 11 additions & 0 deletions lib/adapters/component/create-script.spec.ts
@@ -0,0 +1,11 @@
import { CreateScript } from "./create-script";

describe('Create component', () => {

test('new component', async () => {
const script = await new CreateScript().build('component', 'localhost:8080');
expect(script).toEqual({});
}, 4000);

});

26 changes: 26 additions & 0 deletions lib/adapters/component/create-script.ts
@@ -0,0 +1,26 @@
import { StatusElement } from '../../interfaces';
import { CreateElement } from "../../interfaces";

export class CreateScript implements CreateElement {

public build( name: string, src: string, options?: HTMLScriptElement): Promise<StatusElement> {
const element = document.createElement('script');
element.type = 'text/javascript';
element.src = src;
element.async = true;
this.appendChild(element);
return new Promise((resolve, reject) => {
element.onload = ((e) => resolve({ name, element, status: true } ));
element.onerror = ((e) => {
reject({ name, element, status: false } );
element.remove();
} );
});
}

public appendChild(element: HTMLScriptElement): void {
const documentHead = document.head || document.getElementsByTagName('head')[0];
documentHead.appendChild(element);
}

}
30 changes: 30 additions & 0 deletions lib/adapters/styles/create-style.ts
@@ -0,0 +1,30 @@
import { AdapterServices } from "../../services/adapter.services";
import { StatusElement } from "../../index";
import {AxiosResponse} from "axios";

export class CreateStyle {

public build(name: string, code: string): Promise<StatusElement> {
const element = document.createElement('style');
element.textContent = ` :host { ${ code } }`;
this.appendChild(element);

return new Promise<StatusElement>((resolve, reject) => {
element.onload = ((e) => resolve({ name, status: true, element: element } ));
element.onerror = ((e) => {
reject({ name, status: false, element: element } );
element.remove();
} );
});
}

public appendChild(style: HTMLStyleElement): void {
const documentHead = document.body;
documentHead.appendChild(style);
}

public async sourceServices(src: string): Promise<AxiosResponse> {
const adapterServices = new AdapterServices();
return await adapterServices.textContent(src);
}
}
37 changes: 37 additions & 0 deletions lib/adapters/styles/ui.adapter.ts
@@ -0,0 +1,37 @@
import { BehaviorSubject, Observable } from "rxjs";
import { AxiosResponse } from "axios";
import { BasicElement, ElementDescription, StatusElement } from "../../interfaces";
import { CreateStyle } from "./create-style";

export class UiAdapter {

public allStyles: Array<BasicElement> = [];
private statusStyles: BehaviorSubject<any> = new BehaviorSubject(null);

public getStatusStyles(): Observable<StatusElement> {
return this.statusStyles.asObservable();
}

public create(name: string, src: string): void {
const style = new CreateStyle();
style.sourceServices(src).then( ( res : AxiosResponse) => {
style.build( name, res.data ).then((status: StatusElement) => {
this.statusStyles.next(status);
this.allStyles.push({name: status.name, element: status.element});
}).catch( e => this.statusStyles.next(e));
})
}

public loadStyles(styles: Array<ElementDescription> = []) {
styles.forEach(({ name, src }) => this.create(name, src));
}

public removeOneStyle(name: string): void {
this.allStyles.filter( (component: BasicElement, index: number) => component.name === name)
.forEach( ( { element } : BasicElement)=> element.remove());
}

public allRemoveStyles(): void {
this.allStyles.forEach(( { element } : BasicElement)=> element.remove());
}
}
8 changes: 8 additions & 0 deletions lib/enum/index.ts
@@ -0,0 +1,8 @@
export enum AngularConfig {
zone = 'https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.9.1/zone.min.js',
}

export enum Framework {
angular = 'angular',
reactjs = 'react',
}
5 changes: 5 additions & 0 deletions lib/index.ts
@@ -0,0 +1,5 @@
export * from "./interfaces/global-element";
export * from "./adapters/styles/ui.adapter";
export * from "./adapters/component/component.adapter";
export * from "./adapters/adapter";
export * from "./interfaces/";
9 changes: 9 additions & 0 deletions lib/interfaces/element.ts
@@ -0,0 +1,9 @@
import { StatusElement } from "./global-element";

export interface AppendChild {
appendChild(el: HTMLScriptElement | HTMLStyleElement): void;
}

export interface CreateElement extends AppendChild {
build( src: string, name: string, options?: HTMLScriptElement | HTMLStyleElement ): Promise<StatusElement>;
}
15 changes: 15 additions & 0 deletions lib/interfaces/global-element.ts
@@ -0,0 +1,15 @@
export interface StatusElement {
name: string;
status: boolean;
element: any;
}

export interface ElementDescription {
name: string;
src: string;
}

export interface BasicElement {
name: string;
element: HTMLScriptElement;
}
2 changes: 2 additions & 0 deletions lib/interfaces/index.ts
@@ -0,0 +1,2 @@
export * from './element';
export * from './global-element';
8 changes: 8 additions & 0 deletions lib/services/adapter.services.ts
@@ -0,0 +1,8 @@
import Axios, { AxiosResponse } from 'axios';

export class AdapterServices {

public textContent(src: string, config?: {}): Promise<AxiosResponse> {
return Axios.get(src, config);
}
}
41 changes: 41 additions & 0 deletions package.json
@@ -0,0 +1,41 @@
{
"name": "web-adapter-js",
"version": "0.1.0",
"description": "Web Adapter es un paquete que te permite tener una comunicación dinámica con tus Web Componets externos, ademas podrás cargar tus estilos de forma dinámica.",
"main": "index.js",
"scripts": {
"test": "jest",
"build": "rm -rf dist/ && tsc && cp package.json dist/"
},
"author": {
"name": "Jose Navarro",
"email": "joosenavarro@gmail.com",
"twitter": "joosenunez",
"github": "joosenavarro"
},
"keywords": [
"npm",
"scripts",
"micro frontends",
"web component"
],
"license": "MIT",
"jest": {
"transform": {
"^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json"]
},
"dependencies": {
"axios": "^0.19.2",
"rxjs": "^6.5.4"
},
"devDependencies": {
"@types/jest": "^25.1.3",
"@types/node": "^13.7.7",
"jest": "^25.1.0",
"ts-jest": "^25.2.1",
"typescript": "^3.8.3"
}
}
21 changes: 21 additions & 0 deletions tsconfig.json
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"baseUrl": "./",
"declaration": true, /* Generates corresponding '.d.ts' file. */
"outDir": "dist/", /* Redirect output structure to the directory. */
"strict": true, /* Enable all strict type-checking options. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"forceConsistentCasingInFileNames": true,
"lib": [
"dom",
"es5",
"scripthost",
"es2015.promise",
"es2016"
]
},
"include": ["lib/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
11 changes: 11 additions & 0 deletions tslint.json
@@ -0,0 +1,11 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"indent": [true, "tabs", 2]
},
"rulesDirectory": []
}

0 comments on commit 4cefacb

Please sign in to comment.