-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extension integration #240
Changes from all commits
b9b73be
ffc54a4
e577b3e
22e9dce
7717152
3100b52
082549a
5cb7d35
0a26f65
46a6717
759fef3
e1f1af2
5f0b83d
47820d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,6 +102,7 @@ Slow Reader is a local-first app. Clients do most of the work, and the server ju | |
- See **[`web/README.md`](./web/README.md)** for web client architecture. | ||
- [`server/`](./server/): a small server that syncs data between users’ devices. | ||
- [`proxy/`](./proxy/): HTTP proxy server to bypass censorship or to try web clients before they install the upcoming extensions (to bypass the CORS limit of the web apps). | ||
- [`extension/`](./extension/): browser’s extension to avoid CORS limits in web client. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cross-links between docs |
||
- [`api/`](./api/): types and constants shared between clients and server. | ||
- [`docs/`](./docs/): guides for developers. | ||
- [`scripts/`](./scripts/): scripts to test project and configure Google Cloud. Check the script’s descriptions for further details. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,26 @@ | ||
# Slowreader Extension | ||
|
||
Browser’s extensions to allow web client bypass CORS limit. | ||
|
||
_See the [full architecture guide](../README.md) first._ | ||
|
||
## Scripts | ||
|
||
- `pnpm install` to install dependencies | ||
- `pnpm dev` to run the plugin in development mode | ||
- `pnpm build` to build the plugin for production | ||
- `cd extension && pnpm install` to install dependencies. | ||
- `cd extension && `pnpm start` to run the plugin in development mode. | ||
- `cd extension && pnpm build` to build the plugin for production. | ||
|
||
## Quickstart | ||
## Quick Start | ||
|
||
- Run `pnpm install` to install dependencies | ||
- Run `pnpm dev` to build the extension and watch the changes | ||
- Open `chrome://extensions/` -> `Load unpacked` and choose `dist` folder from this repo. The extension should appear in the list of your extensions | ||
- In the `.env` file of the main app, place the next line (`EXTENSION_ID` can be found in the `ID` line inside the uploaded extension block): | ||
1. Run `cd extension && pnpm start` to build the extension and watch the changes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For steps it is better to use |
||
2. Open `chrome://extensions/` → `Load unpacked` and choose `dist` folder from this repo. The extension should appear in the list of your extensions. | ||
3. In the `.env` file of the main app, place the next line (`EXTENSION_ID` can be found in the `ID` line inside the uploaded extension block): | ||
|
||
`VITE_EXTENSION_ID=<EXTENSION_ID>` | ||
```env | ||
VITE_EXTENSION_ID=EXTENSION_ID | ||
``` | ||
|
||
- Run the main app | ||
4. Run the web client. | ||
|
||
During the development process, you can re-build the extension by clicking on the update button at the right bottom of the extension’s block. | ||
|
||
|
@@ -26,7 +31,7 @@ You can see the console for errors and logs by clicking on the link at the line | |
Connect the extension on application start: | ||
|
||
```ts | ||
const port = chrome.runtime.connect(import.meta.env.VITE_EXTENSION_ID) | ||
let port = chrome.runtime.connect(import.meta.env.VITE_EXTENSION_ID) | ||
``` | ||
|
||
Send messages to the extension to fetch data: | ||
|
@@ -38,7 +43,7 @@ port.postMessage({ | |
}) | ||
|
||
port.onMessage.addListener(response => { | ||
console.log(response.data) // The extension will send the message with fetched data | ||
console.log(response.data) // The extension will send the message | ||
}) | ||
``` | ||
|
||
|
@@ -48,12 +53,11 @@ Check if the extension was disconnected: | |
port.onDisconnect.addListener(() => {}) | ||
``` | ||
|
||
## Publishing | ||
|
||
- Run `pnpm build` to build the production files (will be located in `dist/`) | ||
|
||
- Zip the content of the `dist/` folder | ||
See possible messages in [types API](./api.ts). | ||
|
||
- [Follow this official guide to publish the extension in the Chrome Web Store](https://developer.chrome.com/docs/webstore/publish) | ||
## Publishing | ||
|
||
- After the extension is published in the Chrome Web Store, add the <EXTENSION_ID> of the published extension as a prod env for the main app. | ||
1. Run `pnpm build` to build the production files (will be located in `dist/`) | ||
2. Zip the content of the `dist/` folder | ||
3. Follow [official guide](https://developer.chrome.com/docs/webstore/publish) to publish the extension in the Chrome Web Store. | ||
4. After the extension is published in the Chrome Web Store, add the `EXTENSION_ID` of the published extension as a prod env for the web app. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export type AppMessage = { | ||
options: RequestInit | ||
url: string | ||
} | ||
|
||
export type ExtensionMessage = | ||
| { data: string; type: 'fetched' } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This way of typing messages is better because it tells that |
||
| { error: string; type: 'error' } | ||
| { type: 'connected' } |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,34 +3,32 @@ import { config } from './config.js' | |
|
||
const FETCH_TIMEOUT_MS = 30000 | ||
|
||
const sendMessage = ( | ||
function sendMessage( | ||
port: chrome.runtime.Port, | ||
message: ExtensionMessage | ||
): void => { | ||
): void { | ||
port.postMessage(message) | ||
} | ||
|
||
chrome.runtime.onConnectExternal.addListener(port => { | ||
if (port.sender?.origin === config.HOST) { | ||
sendMessage(port, { type: 'connected' }) | ||
port.onMessage.addListener(async (message: AppMessage) => { | ||
if (message.url) { | ||
await fetch(message.url, { | ||
try { | ||
let response = await fetch(message.url, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By using |
||
...message.options, | ||
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) | ||
}) | ||
.then(data => { | ||
sendMessage(port, { data, type: 'fetched' }) | ||
}) | ||
.catch(error => { | ||
sendMessage(port, { | ||
data: null, | ||
error: error.toString(), | ||
type: 'error' | ||
}) | ||
let data = await response.text() | ||
sendMessage(port, { data, type: 'fetched' }) | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
sendMessage(port, { | ||
error: error.toString(), | ||
type: 'error' | ||
}) | ||
} | ||
} | ||
return true | ||
}) | ||
} | ||
}) |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { defineManifest } from '@crxjs/vite-plugin' | ||
|
||
const URL = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use function instead of static JSON file for manifest |
||
process.env.NODE_ENV === 'production' | ||
? 'https://*.slowreader.app/*' | ||
: 'http://localhost:5173/*' | ||
|
||
export default defineManifest(async () => ({ | ||
background: { | ||
service_worker: 'background.ts', | ||
type: 'module' | ||
}, | ||
description: 'Fetch data from websites for dev.slowreader.app', | ||
externally_connectable: { | ||
matches: [URL] | ||
}, | ||
host_permissions: [URL], | ||
manifest_version: 3, | ||
name: 'Slowreader Extension', | ||
version: '0.0.1' | ||
})) |
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,18 @@ | ||
{ | ||
"name": "slowreader-extension", | ||
"name": "@slowreader/extension", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"engines": { | ||
"node": "^22.6.0", | ||
"pnpm": "^9.0.0" | ||
}, | ||
"scripts": { | ||
"dev": "node build.js dev && vite", | ||
"build": "node build.js prod && vite build" | ||
"start": "vite", | ||
"build": "vite build", | ||
"test": "pnpm build" | ||
}, | ||
"keywords": [], | ||
"author": "[email protected]", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You do not need this keys on |
||
"license": "ISC", | ||
"packageManager": "[email protected]", | ||
"dependencies": { | ||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1" | ||
}, | ||
"devDependencies": { | ||
"@crxjs/vite-plugin": "2.0.0-beta.23", | ||
"@types/react": "^18.2.64", | ||
"@types/react-dom": "^18.2.21", | ||
"@vitejs/plugin-react": "^4.2.1", | ||
"vite": "^5.3.4" | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,8 @@ | ||
import { crx } from '@crxjs/vite-plugin' | ||
import react from '@vitejs/plugin-react' | ||
import path from 'node:path' | ||
import { defineConfig } from 'vite' | ||
|
||
import manifest from './manifest.json' | ||
|
||
const outDir = path.resolve(__dirname, 'dist') | ||
import defineManifest from './manifest.config.ts' | ||
|
||
export default defineConfig({ | ||
build: { | ||
outDir | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You do not need to specify |
||
}, | ||
plugins: [react(), crx({ manifest })] | ||
plugins: [crx({ manifest: defineManifest })] | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We try to avoid calling CI without a reason.
For instance, we do not publish staging on changes irrelevant to web client.