├── .eslintignore ├── .gitignore ├── .prettierrc.yml ├── .eslintrc.yml ├── commitlint.config.js ├── media ├── icon-large.png └── icon-small.png ├── src ├── components │ └── Title.js ├── index.js ├── App.js ├── index.html ├── lib │ ├── prop.js │ └── __tests__ │ │ └── prop.test.js └── webpack-dev-server.js ├── remote-component.config.js ├── webpack.config.js ├── .gitlab-ci.yml ├── .babelrc ├── webpack-demo.config.js ├── LICENSE ├── webpack-dev-server.config.js ├── .vscode └── launch.json ├── webpack-main.config.js ├── changelog.config.js ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /dist 3 | /node_modules 4 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | arrowParens: avoid 2 | tabWidth: 2 3 | trailingComma: none 4 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | extends: ["@paciolan/react"] 2 | rules: 3 | react/prop-types: off 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"] 3 | }; 4 | -------------------------------------------------------------------------------- /media/icon-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paciolan/remote-component-starter/HEAD/media/icon-large.png -------------------------------------------------------------------------------- /media/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paciolan/remote-component-starter/HEAD/media/icon-small.png -------------------------------------------------------------------------------- /src/components/Title.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Title = ({ children }) =>

{children}

; 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint of the Remote Component. 3 | */ 4 | import { App } from "./App"; 5 | 6 | export default App; 7 | -------------------------------------------------------------------------------- /remote-component.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies for Remote Components 3 | */ 4 | 5 | module.exports = { 6 | resolve: { 7 | react: require("react") 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Title } from "./components/Title"; 3 | 4 | export const App = ({ name = "World" }) => { 5 | return Hello {name}!; 6 | }; 7 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpackMainConfig = require("./webpack-main.config"); 2 | const webpackDemoConfig = require("./webpack-demo.config"); 3 | 4 | module.exports = [webpackMainConfig, webpackDemoConfig]; 5 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /src/lib/prop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Safely gets nested properties from an object 3 | * @param {Array} key List of properties 4 | * @param {object} object Object to query 5 | * @returns {*} value 6 | */ 7 | export const prop = ([key, ...rest], object) => 8 | key == null ? object 9 | : object == null ? undefined 10 | : prop(rest, object[key]); // prettier-ignore 11 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: paciolanhub/alpine-node-dev:10.15.3c 2 | 3 | stages: 4 | - build 5 | 6 | Build & Test: 7 | stage: build 8 | except: 9 | - tags 10 | before_script: 11 | - npm ci 12 | script: 13 | - npm run build 14 | - npm run lint 15 | - npm run test:coverage -- --colors 16 | artifacts: 17 | expire_in: 1 hour 18 | paths: 19 | - dist/ 20 | coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/ 21 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "debug": false 8 | } 9 | ], 10 | "@babel/preset-react" 11 | ], 12 | "plugins": [ 13 | [ 14 | "@babel/plugin-transform-runtime", 15 | { 16 | "regenerator": true 17 | } 18 | ], 19 | [ 20 | "@babel/plugin-proposal-class-properties", 21 | { 22 | "loose": true 23 | } 24 | ], 25 | [ 26 | "transform-react-remove-prop-types", 27 | { 28 | "removeImport": true 29 | } 30 | ] 31 | ], 32 | "env": { 33 | "development": { 34 | "sourceMaps": true, 35 | "retainLines": true 36 | }, 37 | "test": { 38 | "sourceMaps": true, 39 | "retainLines": true 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/webpack-dev-server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * webpack-dev-server entry point for debugging. 3 | * This file is not bundled with the library during the build process. 4 | */ 5 | import { RemoteComponent } from "@paciolan/remote-component"; 6 | import React from "react"; 7 | import ReactDOM from "react-dom"; 8 | import LocalComponent from "./index.js"; 9 | 10 | // different paths for localhost vs s3 11 | const url = 12 | process.env.NODE_ENV === "development" ? "/dist/main.js" : "main.js"; 13 | 14 | const node = document.getElementById("app"); 15 | 16 | const Component = props => 17 | process.env.NODE_ENV === "development" 18 | ? 19 | : ; // prettier-ignore 20 | 21 | const App = () => ( 22 | <> 23 | 24 | 25 | ); 26 | 27 | ReactDOM.render(, node); 28 | -------------------------------------------------------------------------------- /webpack-demo.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * generates the dist/index.html and dist/demo.js files 3 | * to demo the component. 4 | */ 5 | 6 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 7 | const path = require("path"); 8 | const webpack = require("webpack"); 9 | 10 | module.exports = { 11 | plugins: [ 12 | new webpack.EnvironmentPlugin({ 13 | "process.env.NODE_ENV": process.env.NODE_ENV 14 | }), 15 | new HtmlWebpackPlugin({ 16 | template: "./src/index.html" 17 | }) 18 | ], 19 | entry: { 20 | demo: "./src/webpack-dev-server.js" 21 | }, 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.m?js$/, 26 | exclude: /(node_modules|bower_components)/, 27 | use: { 28 | loader: "babel-loader" 29 | } 30 | } 31 | ] 32 | }, 33 | resolve: { 34 | alias: { 35 | "remote-component.config.js": path.resolve("./remote-component.config.js") 36 | } 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2019 Paciolan 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /webpack-dev-server.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 2 | const path = require("path"); 3 | const webpack = require("webpack"); 4 | const config = require("./webpack.config"); 5 | 6 | module.exports = { 7 | entry: "./src/webpack-dev-server.js", 8 | plugins: [ 9 | ...config[0].plugins, 10 | new HtmlWebpackPlugin({ 11 | filename: "index.html", 12 | template: "src/index.html" 13 | }), 14 | new webpack.EnvironmentPlugin({ 15 | "process.env.NODE_ENV": process.env.NODE_ENV 16 | }), 17 | new webpack.NamedModulesPlugin(), 18 | new webpack.HotModuleReplacementPlugin() 19 | ], 20 | module: config[0].module, 21 | devServer: { 22 | hot: true, 23 | contentBase: __dirname, 24 | headers: { 25 | "Access-Control-Allow-Origin": "*", 26 | "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", 27 | "Access-Control-Allow-Headers": 28 | "X-Requested-With, content-type, Authorization" 29 | } 30 | }, 31 | resolve: { 32 | alias: { 33 | "remote-component.config.js": path.resolve("./remote-component.config.js") 34 | } 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome", 11 | "url": "http://localhost:9090", 12 | "webRoot": "${workspaceFolder}", 13 | "sourceMaps": true, 14 | }, 15 | { 16 | "name": "Jest", // This is the configuration name you will see in debug sidebar 17 | "type": "node", 18 | "request": "launch", 19 | "port": 9229, 20 | "address": "localhost", 21 | "stopOnEntry": false, 22 | "runtimeExecutable": null, 23 | "env": { 24 | "NODE_ENV": "test" 25 | }, 26 | "console": "integratedTerminal", 27 | "runtimeArgs": [ 28 | "--inspect-brk", // node v8 use debug-brk if older version of node 29 | "${workspaceRoot}/node_modules/.bin/jest", 30 | "--watch", 31 | "--bail", 32 | "--runInBand" 33 | ], 34 | "cwd": "${workspaceRoot}", 35 | "sourceMaps": true 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /src/lib/__tests__/prop.test.js: -------------------------------------------------------------------------------- 1 | const { prop } = require("../prop"); 2 | 3 | describe("lib/prop", () => { 4 | test("prop on undefined returns undefined", () => { 5 | const expected = undefined; 6 | const actual = prop(["a"], undefined); 7 | expect(actual).toBe(expected); 8 | }); 9 | 10 | test("prop on null returns undefined", () => { 11 | const expected = undefined; 12 | const actual = prop(["a"], null); 13 | expect(actual).toBe(expected); 14 | }); 15 | 16 | test("prop[] returns object", () => { 17 | const expected = {}; 18 | const actual = prop([], expected); 19 | expect(actual).toBe(expected); 20 | }); 21 | 22 | test("prop[a] returns object[a]", () => { 23 | const expected = "SUCCESS"; 24 | const actual = prop(["a"], { a: expected }); 25 | expect(actual).toBe(expected); 26 | }); 27 | 28 | test("prop missing prop returns undefined", () => { 29 | const expected = undefined; 30 | const actual = prop(["a", "b"], { a: 123 }); 31 | expect(actual).toBe(expected); 32 | }); 33 | 34 | test("prop nested returns value", () => { 35 | const expected = "SUCCESS"; 36 | const actual = prop(["a", "b"], { a: { b: expected } }); 37 | expect(actual).toBe(expected); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /webpack-main.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * generates: 3 | * - dist/main.js 4 | * - dist/manifest.json 5 | * - dist/webpack-bundle-analyzer-report.html 6 | */ 7 | const webpack = require("webpack"); 8 | const WebpackAssetsManifest = require("webpack-assets-manifest"); 9 | const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); 10 | const remoteComponentConfig = require("./remote-component.config").resolve; 11 | 12 | const externals = Object.keys(remoteComponentConfig).reduce( 13 | (obj, key) => ({ ...obj, [key]: key }), 14 | {} 15 | ); 16 | 17 | module.exports = { 18 | plugins: [ 19 | new webpack.EnvironmentPlugin({ 20 | "process.env.NODE_ENV": process.env.NODE_ENV 21 | }), 22 | new BundleAnalyzerPlugin({ 23 | analyzerMode: "static", 24 | openAnalyzer: false, 25 | reportFilename: "webpack-bundle-analyzer-report.html" 26 | }), 27 | new WebpackAssetsManifest() 28 | ], 29 | entry: { 30 | main: "./src/index.js" 31 | }, 32 | output: { 33 | libraryTarget: "commonjs" 34 | }, 35 | externals: { 36 | ...externals, 37 | "remote-component.config.js": "remote-component.config.js" 38 | }, 39 | module: { 40 | rules: [ 41 | { 42 | test: /\.m?js$/, 43 | exclude: /(node_modules|bower_components)/, 44 | use: { 45 | loader: "babel-loader" 46 | } 47 | } 48 | ] 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /changelog.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | list: [ 3 | "test", 4 | "feat", 5 | "fix", 6 | "chore", 7 | "docs", 8 | "refactor", 9 | "style", 10 | "ci", 11 | "perf" 12 | ], 13 | maxMessageLength: 64, 14 | minMessageLength: 3, 15 | questions: [ 16 | "type", 17 | "scope", 18 | "subject", 19 | "body", 20 | "breaking", 21 | "issues", 22 | "lerna" 23 | ], 24 | scopes: [], 25 | types: { 26 | chore: { 27 | description: "Build process or auxiliary tool changes", 28 | emoji: "🛠️", 29 | value: "chore" 30 | }, 31 | ci: { 32 | description: "CI related changes", 33 | emoji: "🤖", 34 | value: "ci" 35 | }, 36 | docs: { 37 | description: "Documentation only changes", 38 | emoji: "📚", 39 | value: "docs" 40 | }, 41 | feat: { 42 | description: "A new feature", 43 | emoji: "✨", 44 | value: "feat" 45 | }, 46 | fix: { 47 | description: "A bug fix", 48 | emoji: "🐛", 49 | value: "fix" 50 | }, 51 | perf: { 52 | description: "A code change that improves performance", 53 | emoji: "⚡️", 54 | value: "perf" 55 | }, 56 | refactor: { 57 | description: "A code change that neither fixes a bug or adds a feature", 58 | emoji: "💡", 59 | value: "refactor" 60 | }, 61 | release: { 62 | description: "Create a release commit", 63 | emoji: "🚀", 64 | value: "release" 65 | }, 66 | style: { 67 | description: "Markup, white-space, formatting, missing semi-colons...", 68 | emoji: "🎨", 69 | value: "style" 70 | }, 71 | test: { 72 | description: "Adding missing tests", 73 | emoji: "🚨", 74 | value: "test" 75 | } 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@paciolan/remote-component-starter", 3 | "version": "1.0.0-semantic-versioning", 4 | "description": "Remote Component Starter", 5 | "private": true, 6 | "browser": "dist/main.js", 7 | "author": "Paciolan", 8 | "license": "MIT", 9 | "scripts": { 10 | "build": "npm run clean && cross-env NODE_ENV=production webpack --mode production", 11 | "build:dev": "npm run clean && cross-env NODE_ENV=development webpack --mode development", 12 | "webpack-dev-server": "cross-env NODE_ENV=development webpack-dev-server -d --port 9090 --config webpack-dev-server.config.js --open", 13 | "start": "concurrently -n webpack,webpack-dev-server -c green,cyan \"npm run build:dev -- --watch --verbose\" \"npm run webpack-dev-server\"", 14 | "clean": "rimraf dist", 15 | "cz": "git-cz", 16 | "test": "echo no tests", 17 | "test:changed": "npm run test -- --changedSince HEAD", 18 | "test:coverage": "npm run test -- --coverage", 19 | "lint": "eslint ." 20 | }, 21 | "dependencies": { 22 | "react": "^16.14.0" 23 | }, 24 | "devDependencies": { 25 | "@babel/cli": "^7.12.10", 26 | "@babel/core": "^7.12.10", 27 | "@babel/plugin-proposal-class-properties": "^7.12.1", 28 | "@babel/plugin-transform-runtime": "^7.12.10", 29 | "@babel/preset-env": "^7.12.10", 30 | "@babel/preset-react": "^7.12.10", 31 | "@babel/runtime": "^7.12.5", 32 | "@commitlint/cli": "^11.0.0", 33 | "@commitlint/config-conventional": "^11.0.0", 34 | "@paciolan/eslint-config-react": "^1.0.4", 35 | "@paciolan/remote-component": "^2.10.2", 36 | "babel-eslint": "^10.1.0", 37 | "babel-loader": "^8.2.2", 38 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 39 | "concurrently": "^5.3.0", 40 | "core-js": "^2.6.12", 41 | "cross-env": "^7.0.3", 42 | "eslint": "^7.15.0", 43 | "eslint-config-prettier": "^6.15.0", 44 | "eslint-plugin-babel": "^5.3.1", 45 | "eslint-plugin-prettier": "^3.3.0", 46 | "eslint-plugin-react": "^7.21.5", 47 | "git-cz": "^4.7.6", 48 | "html-webpack-plugin": "^4.5.0", 49 | "husky": "^4.3.6", 50 | "prettier": "^2.2.1", 51 | "react-dom": "^16.14.0", 52 | "regenerator-runtime": "^0.13.7", 53 | "rimraf": "^3.0.2", 54 | "webpack": "^4.44.2", 55 | "webpack-assets-manifest": "^3.1.1", 56 | "webpack-bundle-analyzer": "^3.9.0", 57 | "webpack-cli": "^3.3.12", 58 | "webpack-dev-server": "^3.11.0" 59 | }, 60 | "husky": { 61 | "hooks": { 62 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 63 | "pre-commit": "npm run lint && npm run build && npm run test:changed", 64 | "pre-push": "npm run test:coverage" 65 | } 66 | }, 67 | "config": { 68 | "commitizen": { 69 | "path": "./node_modules/git-cz" 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Remote Component Starter Kit 2 | 3 | ![Starter Kit](https://raw.githubusercontent.com/Paciolan/remote-component-starter/master/media/icon-small.png) 4 | 5 | Starter Kit for quickly creating a Remote React Component that can be Remotely Loaded by `@paciolan/remote-component`. 6 | 7 | ## Getting Started 8 | 9 | Clone the repository and initialize your project 10 | 11 | ```bash 12 | # create new repo 13 | mkdir my-component 14 | cd my-component 15 | git init 16 | 17 | # pull the remote component starter kit 18 | git pull https://github.com/Paciolan/remote-component-starter.git --depth=1 19 | git commit --amend -m "chore: 🛠️ pull remote-component-starter" 20 | 21 | # install dependencies 22 | npm ci 23 | ``` 24 | 25 | Modify `package.json` and replace the starter kit values with your own. 26 | 27 | - set `name` to the name of your project. 28 | - set `description` to describe your project. 29 | - set `repository` to point to your repository. 30 | - set `license` to reflect the license of your project. 31 | 32 | ## Files 33 | 34 | There are a few important files, one set is used for the bundle, another set for local development. 35 | 36 | - `src/index.js` - Entrypoint of the Remote Component. The component needs to be the `default` export. 37 | - `src/webpack-dev-server.js` - Entrypoint for `webpack-dev-server`. This is only used for development and will not be included in the final bundle. 38 | - `src/index.html` - HTML for `webpack-dev-server`. This is only used for development and will not be included in the final bundle. 39 | 40 | ## Building 41 | 42 | The bundle will be output to the `dist/main.js`. 43 | 44 | ```bash 45 | npm run build 46 | ``` 47 | 48 | Create a development build for easier debugging. 49 | 50 | ```bash 51 | npm run build:dev 52 | ``` 53 | 54 | ## Debugging 55 | 56 | The component can be debugged locally by first starting `webpack-dev-server`. 57 | 58 | ```bash 59 | npm run start 60 | ``` 61 | 62 | Now (using VSCODE), go to the Debug tab, select "Launch Chrome" and start the debugger (F5). 63 | 64 | You should now be able to set breakpoints and step through the code. 65 | 66 | ## Changing the Output 67 | 68 | The bundle as a default will be output to the `dist/main.js`. This can be updated by changing the following two files: 69 | 70 | 1. `entry` in `webpack-main.config.js`. Update the `main` property to a desired output name. 71 | 72 | ```javascript 73 | module.exports = { 74 | ... 75 | entry: { 76 | main: "./src/index.js" 77 | }, 78 | ... 79 | }; 80 | ``` 81 | 82 | 2. `url` variable in `src/webpac-dev-server.js` 83 | 84 | ```javascript 85 | // different paths for localhost vs s3 86 | const url = 87 | global.location.hostname === "localhost" ? "/dist/main.js" : "main.js"; 88 | ``` 89 | 90 | ## External Dependencies 91 | 92 | The Remote Component is self contained with all of it's dependencies bundled with webpack. Any dependencies that will be provided by the app should be marked as `external` in the `webpack.config.js`. 93 | 94 | In this example, `react` and `prop-types` are added to `externals`. They will not be included in the bundle. The web application is expected to provide these dependencies. 95 | 96 | ```javascript 97 | module.exports = { 98 | output: { 99 | libraryTarget: "commonjs" 100 | }, 101 | externals: { 102 | react: "react", 103 | "prop-types": "prop-types" 104 | } 105 | }; 106 | ``` 107 | 108 | ## Commiting 109 | 110 | Commits are added to the repository with commitizen compatible `git-cz`. 111 | 112 | ```bash 113 | # stage all changes 114 | git add . 115 | 116 | # run commitizen 117 | npm run cz 118 | ``` 119 | 120 | ## Contributors 121 | 122 | Joel Thoms (https://twitter.com/joelnet) 123 | 124 | Icon made by [Freepik](https://www.flaticon.com/authors/freepik) from [www.flaticon.com](www.flaticon.com) 125 | --------------------------------------------------------------------------------