Skip to content

Commit

Permalink
Adds Slowreader Chrome extension (#234)
Browse files Browse the repository at this point in the history
* Create Slowreader Chrome extension

* Deleted unused var

* Adds description

* Deleted files, added build script for manifest.json

* Fix lint warnings

* Change to import.meta.dirname

* Update Node.js

---------

Co-authored-by: Andrey Sitnik <[email protected]>
  • Loading branch information
ninaTorgunakova and ai committed Aug 14, 2024
1 parent 1b583ec commit 3d959fc
Show file tree
Hide file tree
Showing 19 changed files with 254 additions and 1 deletion.
3 changes: 2 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export default [
'web/dist/',
'web/storybook-static/',
'proxy/dist/',
'web/vite.config.ts.*'
'web/vite.config.ts.*',
'extension/dist/'
]
},
...loguxSvelteConfig,
Expand Down
1 change: 1 addition & 0 deletions extension/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_HOST=http://localhost:5173
1 change: 1 addition & 0 deletions extension/.env.dev.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_HOST=http://localhost:5173
1 change: 1 addition & 0 deletions extension/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_HOST=https://dev.slowreader.app
2 changes: 2 additions & 0 deletions extension/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
59 changes: 59 additions & 0 deletions extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Slowreader Extension

## Scripts

- `pnpm install` to install dependencies
- `pnpm dev` to run the plugin in development mode
- `pnpm build` to build the plugin for production

## Quickstart

- 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):

`VITE_EXTENSION_ID=<EXTENSION_ID>`

- Run the main app

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.

You can see the console for errors and logs by clicking on the link at the line `Inspect views: service worker` in the extension’s block.

## Using the Extension in the Main Application

Connect the extension on application start:

```ts
const port = chrome.runtime.connect(import.meta.env.VITE_EXTENSION_ID)
```

Send messages to the extension to fetch data:

```ts
port.postMessage({
url: 'https://example-url',
options: { method: 'GET' }
})

port.onMessage.addListener(response => {
console.log(response.data) // The extension will send the message with fetched data
})
```

Check if the extension was disconnected:

```ts
port.onDisconnect.addListener(() => {})
```

## Publishing

- Run `pnpm build` to build the production files (will be located in `dist/`)

- Zip the content of the `dist/` folder

- [Follow this official guide to publish the extension in the Chrome Web Store](https://developer.chrome.com/docs/webstore/publish)

- 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.
18 changes: 18 additions & 0 deletions extension/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import fs from 'node:fs'
import path from 'node:path'

function prepareManifest(env) {
let manifestFile = `manifest.${env}.json`

let srcPath = path.join(import.meta.dirname, manifestFile)
let destPath = path.join(import.meta.dirname, 'manifest.json')

fs.copyFileSync(srcPath, destPath)
}

const env = process.argv[2]
if (!env) {
process.exit(1)
}

prepareManifest(env)
14 changes: 14 additions & 0 deletions extension/manifest.dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"manifest_version": 3,
"name": "Slowreader Extension",
"description": "Fetch data from websites for dev.slowreader.app",
"version": "0.0.1",
"background": {
"service_worker": "src/background.ts",
"type": "module"
},
"externally_connectable": {
"matches": ["http://localhost:5173/*"]
},
"host_permissions": ["http://localhost:5173/*"]
}
14 changes: 14 additions & 0 deletions extension/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"manifest_version": 3,
"name": "Slowreader Extension",
"description": "Fetch data from websites for dev.slowreader.app",
"version": "0.0.1",
"background": {
"service_worker": "src/background.ts",
"type": "module"
},
"externally_connectable": {
"matches": ["http://localhost:5173/*"]
},
"host_permissions": ["http://localhost:5173/*"]
}
14 changes: 14 additions & 0 deletions extension/manifest.prod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"manifest_version": 3,
"name": "Slowreader Extension",
"description": "Fetch data from websites for dev.slowreader.app",
"version": "0.0.1",
"background": {
"service_worker": "src/background.ts",
"type": "module"
},
"externally_connectable": {
"matches": ["https://dev.slowreader.app/*"]
},
"host_permissions": ["https://dev.slowreader.app/*"]
}
29 changes: 29 additions & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"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"
},
"keywords": [],
"author": "[email protected]",
"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"
}
}
14 changes: 14 additions & 0 deletions extension/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type AppMessage = {
id?: string
options: RequestInit
url: string
}

type ExtensionMessageType = 'connected' | 'error' | 'fetched'

export type ExtensionMessage = {
data?: null | Response
error?: string
id?: string
type: ExtensionMessageType
}
36 changes: 36 additions & 0 deletions extension/src/background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { AppMessage, ExtensionMessage } from './api.js'
import { config } from './config.js'

const FETCH_TIMEOUT_MS = 30000

const sendMessage = (
port: chrome.runtime.Port,
message: ExtensionMessage
): 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, {
...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'
})
})
}
return true
})
}
})
3 changes: 3 additions & 0 deletions extension/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const config = {
HOST: import.meta.env.VITE_HOST
}
1 change: 1 addition & 0 deletions extension/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
15 changes: 15 additions & 0 deletions extension/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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')

export default defineConfig({
build: {
outDir
},
plugins: [react(), crx({ manifest })]
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"devDependencies": {
"@logux/eslint-config": "53.3.0",
"@types/node": "22.1.0",
"@types/chrome": "^0.0.269",
"@types/ws": "8.5.12",
"better-node-test": "0.6.0",
"eslint": "9.8.0",
Expand Down
28 changes: 28 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"moduleResolution": "NodeNext",
"types": ["chrome"],
"esModuleInterop": true,
"skipLibCheck": true,
"allowJs": true,
Expand Down

0 comments on commit 3d959fc

Please sign in to comment.