diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index 7d3415b..0000000 --- a/.browserslistrc +++ /dev/null @@ -1,4 +0,0 @@ -maintained node versions -last 1 version -> 5% -not dead \ No newline at end of file diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 387f3b1..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -tests/integrations/rimless.min.js diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index fd2082a..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "worker": true, - "jest": true - }, - "extends": [ - "plugin:react/recommended", - "airbnb" - ], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": 2018, - "sourceType": "module" - }, - "plugins": [ - "react", - "@typescript-eslint" - ], - "settings": { - "import/resolver": { - "node": { - "extensions": [ - ".js", - ".jsx", - ".ts", - ".tsx" - ] - } - } - }, - "rules": { - "@typescript-eslint/quotes": [ - "warn", - "double", - { - "allowTemplateLiterals": true - } - ], - "arrow-body-style": "off", - "arrow-parens": [ - "warn", - "always" - ], - "class-methods-use-this": "off", - "consistent-return": "off", - "function-paren-newline": "off", - "implicit-arrow-linebreak": "off", - "import/extensions": "off", - "react/jsx-filename-extension": "off", - "no-unused-vars": "off", - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": [ - "docs/**/*", - "**/stories/**/*.js", - "**/*test.ts", - "**/*test.tsx", - "**/*stories.tsx", - "src/setupTests.js", - "**/*__mocks__/**", - "rollup.config.js" - ] - } - ], - "react/self-closing-comp": [ - "error", - { - "component": true, - "html": true - } - ], - "jsx-a11y/label-has-associated-control": "off", - "react/jsx-props-no-spreading": "off", - "react/destructuring-assignment": "off", - "import/prefer-default-export": "off", - "indent": "off", - "lines-between-class-members": "off", - "no-param-reassign": "off", - "no-plusplus": "off", - "no-shadow": "off", - "no-restricted-globals": "off", - "object-curly-newline": "off", - "operator-linebreak": "off", - "padded-blocks": "off", - "quotes": [ - "warn", - "double", - { - "allowTemplateLiterals": true - } - ], - "template-tag-spacing": "off" - } -} \ No newline at end of file diff --git a/.github/workflows/test-and-build.yml b/.github/workflows/test-and-build.yml new file mode 100644 index 0000000..a0974a6 --- /dev/null +++ b/.github/workflows/test-and-build.yml @@ -0,0 +1,15 @@ +name: Test and build when pushing to a branch +on: push +jobs: + build_and_deploy: + runs-on: ubuntu-latest + env: + NODE_OPTIONS: "--max_old_space_size=4096" + steps: + - uses: actions/checkout@v2 + - name: install monorepo dependencies + run: npm ci + - name: build monorepo packages + run: npm run build + - name: test monorepo + run: npm run test diff --git a/.gitignore b/.gitignore index b445a06..3f30fe9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,28 @@ -# dependencies -node_modules -.pnp -.pnp.js - -# testing -coverage -dependency-map - -# production -build -lib -storybook-static - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - +# Logs +logs +*.log npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? -# storybook -.awcache +*storybook.log -# dependency map -dependency-map.html \ No newline at end of file +*.tsbuildinfo \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0b377a4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{ "printWidth": 120 } diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 0e98ab8..0000000 --- a/.storybook/main.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - stories: ["../docs/**/*.stories.tsx", "../docs/**/*.stories.mdx"], - addons: ["@storybook/addon-docs"], - webpackFinal: async (config) => { - config.module.rules.push({ - test: /\.(ts|tsx)$/, - use: [ - { - loader: require.resolve("awesome-typescript-loader"), - options: { - configFileName: "./tsconfig.dev.json", - transpileOnly: true, - useCache: true, - }, - } - ], - }); - config.resolve.extensions.push(".ts", ".tsx"); - return config; - }, -}; diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 0000000..88bf8bb --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,17 @@ +import type { StorybookConfig } from "@storybook/react-vite"; + +const config: StorybookConfig = { + stories: ["../docs/**/*.mdx", "../docs/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + addons: [ + "@storybook/addon-onboarding", + "@storybook/addon-links", + "@storybook/addon-essentials", + "@chromatic-com/storybook", + "@storybook/addon-interactions", + ], + framework: { + name: "@storybook/react-vite", + options: {}, + }, +}; +export default config; diff --git a/.storybook/manager.js b/.storybook/manager.js deleted file mode 100644 index a044194..0000000 --- a/.storybook/manager.js +++ /dev/null @@ -1,6 +0,0 @@ -import { addons } from "@storybook/addons"; -import theme from "./theme"; - -addons.setConfig({ - theme, -}); diff --git a/.storybook/preview.ts b/.storybook/preview.ts new file mode 100644 index 0000000..37914b1 --- /dev/null +++ b/.storybook/preview.ts @@ -0,0 +1,14 @@ +import type { Preview } from "@storybook/react"; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/.storybook/theme.js b/.storybook/theme.js deleted file mode 100644 index 567d908..0000000 --- a/.storybook/theme.js +++ /dev/null @@ -1,8 +0,0 @@ -import { create } from "@storybook/theming/create"; - -export default create({ - base: "light", - brandTitle: "rimless", - brandUrl: "https://github.com/au-re/rimless", - brandImage: "https://raw.githubusercontent.com/au-re/rimless/master/assets/icon.png", -}); \ No newline at end of file diff --git a/README.md b/README.md index 22b4403..94dd6fd 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,19 @@ [![npm][npm-image]][npm-url] [![Commitizen friendly][commitizen-image]][commitizen-url] +![npm bundle size](https://img.shields.io/bundlephobia/minzip/rimless) > Rimless makes event based communication easy with a promise-based API wrapping [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). Works with both **iframes** and **webworkers**. You can use `rimless` to call remote procedures, exchange data or expose local functions with **iframes**/**webworkers**. -You can see it in action here https://au-re.github.io/rimless. +You can see it in action in the code sandbox below: + + + +![CodeSandbox](https://img.shields.io/badge/Codesandbox-040404?style=for-the-badge&logo=codesandbox&logoColor=DBDBDB) + + ## Installation @@ -38,39 +45,42 @@ or from a CDN **in the host website** ```js -import { host } from "rimless"; +import { host } from "rimless"; const connection = await host.connect(iframe, { - myVariable: 12, - myFunction: (value) => `hello ${value}`, + someHostVariable: 12, + someHostFunction: (value) => `hello ${value}`, }); // access variables on the iframe -console.log(connection.remote.myIframeVariable); // 42 +console.log(connection.remote.someGuestVariable); // 42 // call remote procedures on the iframe -const result = await connection.remote.myIframeFunction("here"); -console.log(result); // hello here +const result = await connection.remote.someGuestFunction("here"); + +console.log(result); // hello here // close the connection connection.close(); ``` + **in the iframe** ```js -import { guest } from "rimless"; +import { guest } from "rimless"; const connection = await guest.connect({ - myIframeVariable: 42, - myIframeFunction: (value) => `hello ${value}`, + someGuestVariable: 42, + someGuestFunction: (value) => `hello ${value}`, }); // access variables on the host -console.log(connection.remote.myVariable); // 12 +console.log(connection.remote.someHostVariable); // 12 // call remote procedures on host -const res = await connection.remote.myFunction("there"); -console.log(res); // hello there +const res = await connection.remote.someHostFunction("there"); + +console.log(res); // hello there // close the connection connection.close(); @@ -83,7 +93,7 @@ connection.close(); This is how you can **connect your website** to an iframe or webworker: ```js -import { host } from "rimless"; +import { host } from "rimless"; const iframe = document.getElementById("myIframe"); const worker = new Worker("myWorker"); @@ -100,7 +110,7 @@ You also need to **connect your iframe/webworker** to the host website. Usage from an iframe: ```js -import { guest } from "rimless"; +import { guest } from "rimless"; // connect to the parent website guest.connect(); @@ -122,7 +132,7 @@ guest.connect(); To do anything meaningful with this connection you need to provide a schema that defines **the API** of the host/iframe/webworker. Any serializeable values as well as functions are ok to use. In the example below the host website provides a function that will update its background color when invoked. ```js -import { host } from "rimless"; +import { host } from "rimless"; const api = { setColor: (color) => { @@ -142,7 +152,7 @@ The api schema must be passed on connection, the same applies to the `iframe/web With the host API exposed we can now invoke the remote procedure from the iframe. ```js -import { guest } from "rimless"; +import { guest } from "rimless"; // connect returns a promise that resolves in a connection object // `connection.remote` contains the api you can invoke @@ -156,12 +166,13 @@ guest.connect().then((connection) => { Closing a connection will remove all event listeners that were registered. ```js -import { guest } from "rimless"; +import { guest } from "rimless"; guest.connect().then((connection) => { connection.close(); }); ``` + --- ## How it Works @@ -200,7 +211,7 @@ This library is inspired by [Postmate](https://www.npmjs.com/package/postmate) a ## API -Rimless exports two objects: `host` and `guest`. +Rimless exports two objects: `host` and `guest`. > ### `host.connect` @@ -208,16 +219,15 @@ Connect your website to a "guest" (iframe/webworker). ```js host.connect(iframe, { - log: (value) => console.log(value) + log: (value) => console.log(value), }); ``` -| Name | Type | Description | Required | -| --- | --- | --- | --- | -| `guest` | `HTMLIFrameElement` or `Worker` | Target of the connection | required | -| `schema` | `object` | schema of the api you want to expose | - | -| `options` | `object` | - | - | - +| Name | Type | Description | Required | +| --------- | ------------------------------- | ------------------------------------ | -------- | +| `guest` | `HTMLIFrameElement` or `Worker` | Target of the connection | required | +| `schema` | `object` | schema of the api you want to expose | - | +| `options` | `object` | - | - | > ### `guest.connect` @@ -225,28 +235,17 @@ Connect a "guest" to your website. The guest connection automatically happens ba ```js guest.connect({ - log: (value) => console.log(value) + log: (value) => console.log(value), }); ``` -| Name | Type | Description | Default | -| --- | --- | --- | --- | -| `schema` | `object` | schema of the api you want to expose | - | -| `options` | `object` | - | - | +| Name | Type | Description | Default | +| --------- | -------- | ------------------------------------ | ------- | +| `schema` | `object` | schema of the api you want to expose | - | +| `options` | `object` | - | - | --- -## Contributing - -We use the [airbnb style guide](https://github.com/airbnb/javascript) when writing javascript, with -some minor modifications. Make sure eslint is installed and running before making changes, as it -will ensure your coding style matches that of the project. - -We use [commitizen](https://github.com/commitizen/cz-cli) and -[angular's conventional changelog](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) -to enforce a consistent commit format. When writing commits, make sure you run `npm run commit` -instead of `git commit`. - ## License [MIT](license-url) diff --git a/docs/GettingStarted.mdx b/docs/GettingStarted.mdx new file mode 100644 index 0000000..5aebe25 --- /dev/null +++ b/docs/GettingStarted.mdx @@ -0,0 +1,20 @@ +import { Meta } from "@storybook/blocks"; +import SingleIframeExample from "./examples/SingleIframeExample"; + + + +# Rimless + +Rimless makes event based communication easy with a promise-based API wrapping [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). + +Rimless works with both **iframes** and **webworkers**. + +You can use `Rimless` to call remote procedures, exchange data or expose local functions +with **iframes**/**webworkers**. + +## RPCs on Iframes + +In the example below you can invoke remote procedures from an iframe to the host website and vice +versa. + + diff --git a/docs/GettingStarted.stories.mdx b/docs/GettingStarted.stories.mdx deleted file mode 100644 index ac9c798..0000000 --- a/docs/GettingStarted.stories.mdx +++ /dev/null @@ -1,20 +0,0 @@ -import { Meta, Story, Preview } from "@storybook/addon-docs/blocks"; -import SingleIframeExample from "./examples/SingleIframeExample"; - - - -# Rimless - -> Rimless makes event based communication easy with a promise-based API wrapping [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). Works with both **iframes** and **webworkers**. - -You can use `Rimless` to call remote procedures, exchange data or expose local functions -with **iframes**/**webworkers**. - -## RPCs on Iframes - -In the example below you can invoke remote procedures from an iframe to the host website and vice -versa. - - - - diff --git a/docs/examples/Components.ts b/docs/examples/Components.ts deleted file mode 100644 index 6a76b27..0000000 --- a/docs/examples/Components.ts +++ /dev/null @@ -1,20 +0,0 @@ -import styled from "styled-components"; - -export const Background = styled.div` - display: flex; - flex-direction: column; - max-width: 720px; - margin: 0 auto; - padding: 1rem; - font-family: Oswald, sans-serif; - - button { - width: 120px; - } -`; - -export const Iframe = styled.iframe` - height: 240px; - width: 240px; - border: 1px solid #FFF; -`; diff --git a/docs/examples/SingleIframeExample.tsx b/docs/examples/SingleIframeExample.tsx index e752a40..316ec47 100644 --- a/docs/examples/SingleIframeExample.tsx +++ b/docs/examples/SingleIframeExample.tsx @@ -1,7 +1,8 @@ import React from "react"; -import { Background, Iframe } from "./Components"; +import template from "./iframe.html?raw"; import { host } from "../../src/index"; +import { IConnection } from "../../src/types"; function makeRandomColor() { const letters = "0123456789ABCDEF"; @@ -13,13 +14,13 @@ function makeRandomColor() { } function SingleIframeExample() { - const iframe = React.useRef(null); - const [color, setColor] = React.useState(null); - const [connection, setConnection] = React.useState(null); + const iframe = React.useRef(null); + const [color, setColor] = React.useState("#fff"); + const [connection, setConnection] = React.useState(null); React.useEffect(() => { - if (!iframe.current) return; (async () => { + if (!iframe?.current) return; const newConnection = await host.connect(iframe.current, { setColor, }); @@ -28,24 +29,34 @@ function SingleIframeExample() { }, [iframe.current]); return ( - - +

HOST

-
-
-