Skip to content

Commit

Permalink
Merge pull request #3 from Gunock/feature/crxjs-2
Browse files Browse the repository at this point in the history
Feature/crxjs 2
  • Loading branch information
Gunock authored Mar 26, 2024
2 parents e3317a8 + efc0ebb commit 4ad893c
Show file tree
Hide file tree
Showing 21 changed files with 381 additions and 145 deletions.
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"jsxSingleQuote": false,
"trailingComma": "none",
"bracketSameLine": false,
"bracketSpacing": false
"bracketSpacing": true
}
51 changes: 25 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

# Chrome Extension (MV3) Boilerplate with React 18 and Vite 5

[//]: # 'TODO: Publish the template on NPM'
[//]: # '[![npm](https://img.shields.io/npm/v/chrome-extension-boilerplate-react)](https://www.npmjs.com/package/chrome-extension-boilerplate-react)'
[//]: # '[![npm-download](https://img.shields.io/npm/dw/chrome-extension-boilerplate-react)](https://www.npmjs.com/package/chrome-extension-boilerplate-react)'
[//]: # '[![npm](https://img.shields.io/npm/dm/chrome-extension-boilerplate-react)](https://www.npmjs.com/package/chrome-extension-boilerplate-react)'
[//]: # (# 'TODO: Publish the template on NPM')

[//]: # ([![npm](https://img.shields.io/npm/v/chrome-extension-boilerplate-react)](https://www.npmjs.com/package/chrome-extension-boilerplate-react))

[//]: # ([![npm-download](https://img.shields.io/npm/dw/chrome-extension-boilerplate-react)](https://www.npmjs.com/package/chrome-extension-boilerplate-react))

[//]: # ([![npm](https://img.shields.io/npm/dm/chrome-extension-boilerplate-react)](https://www.npmjs.com/package/chrome-extension-boilerplate-react))

This repository contains a boilerplate for building Chrome Extensions with React 18, TypeScript, and Vite 5.
This boilerplate is inspired by and adapted
Expand All @@ -14,21 +17,19 @@ from [chrome-extension-boilerplate-react](https://github.com/lxieyang/chrome-ext
## Features

This is a basic Chrome Extensions boilerplate to help you write modular and modern Javascript code and load CSS easily.
This boilerplate is updated with:

- [Chrome Extension Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/)
- [React 18](https://reactjs.org)
- [MUI](https://mui.com/)
- ESLint:
- [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react)
- [eslint-config-prettier](https://www.npmjs.com/package/eslint-config-prettier)
- [eslint-plugin-simple-import-sort](https://www.npmjs.com/package/eslint-plugin-simple-import-sort)
- [typescript-eslint](https://www.npmjs.com/package/typescript-eslint)
- [Prettier](https://prettier.io/)
- [TypeScript](https://www.typescriptlang.org/)

I have avoided using CRXJS Vite Plugin on purpose as it's last update was in 2022, and it could possibly have some
issues with newer versions of Vite.
This boilerplate is using:

- [Chrome Extension Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/)
- [React 18](https://reactjs.org)
- [MUI](https://mui.com/)
- [CRXJS Vite Plugin](https://www.npmjs.com/package/@crxjs/vite-plugin/v/2.0.0-beta.23)
- ESLint:
- [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react)
- [eslint-config-prettier](https://www.npmjs.com/package/eslint-config-prettier)
- [eslint-plugin-simple-import-sort](https://www.npmjs.com/package/eslint-plugin-simple-import-sort)
- [typescript-eslint](https://www.npmjs.com/package/typescript-eslint)
- [Prettier](https://prettier.io/)
- [TypeScript](https://www.typescriptlang.org/)

Please open up an issue to nudge me to keep the npm packages up-to-date.

Expand All @@ -39,9 +40,9 @@ Please open up an issue to nudge me to keep the npm packages up-to-date.
1. Check if your [Node.js](https://nodejs.org/) version is >= **18**.
2. Clone this repository.
3. Change the package's `name`, `description`, and `repository` fields in `package.json`.
4. Change the name of your extension on `src/manifest.json`.
4. Change the name of your extension in `manifest.json`.
5. Run `yarn install` to install the dependencies.
6. Run `yarn dist`
6. Run `yarn build`
7. Load your extension on Chrome following:
1. Access `chrome://extensions/`
2. Turn the `Developer mode` switch on (top right corner)
Expand All @@ -64,24 +65,22 @@ This boilerplate supports TypeScript! Everything that can be written in TypeScri

## Change Watchers

This boilerplate has watch scripts for the popup (`yarn watch:popup`), background script (`yarn watch:background`), and
content script (`yarn watch:content`).
`yarn dist` has to be run first to copy assets, `manifest.json` and Chrome extension files to the `dist` folder.
This boilerplate has a watch script (`yarn watch`) which will update the extension's code every time you save a file.

## Packing

After the development of your extension run the command

```
$ yarn dist
$ yarn build
```

Now, the content of `dist` folder will be the extension ready to be submitted to the Chrome Web Store. Just take a look
at the [official guide](https://developer.chrome.com/webstore/publish) to more infos about publishing.

## Resources:

- [Chrome Extension documentation](https://developer.chrome.com/extensions/getstarted)
- [Chrome Extension documentation](https://developer.chrome.com/extensions/getstarted)

---

Expand Down
4 changes: 2 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export default tseslint.config(
...tseslint.configs.recommended,
reactRecommended,
reactJsxRuntime,
eslintConfigPrettier,
{
plugins: {
'simple-import-sort': simpleImportSort
Expand All @@ -24,5 +23,6 @@ export default tseslint.config(
'simple-import-sort/imports': 'warn',
'simple-import-sort/exports': 'warn'
}
}
},
eslintConfigPrettier
);
6 changes: 3 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
},
"action": {
"default_title": "Chrome Extension React",
"default_popup": "popup/index.html",
"default_popup": "src/popup/index.html",
"default_icon": "assets/icons/icon-34.png"
},
"content_scripts": [
{
"js": ["content/index.js"],
"js": ["src/content/index.ts"],
"matches": ["*://*/*"]
}
],
"background": {
"service_worker": "background/index.js"
"service_worker": "src/background/index.ts"
},
"web_accessible_resources": [
{
Expand Down
15 changes: 3 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,8 @@
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"watch": "yarn dist && yarn watch:popup",
"watch:background": "vite --config vite.config.background.ts build --watch",
"watch:content": "vite --config vite.config.content.ts build --watch",
"watch:popup": "vite --config vite.config.popup.ts build --watch",
"build": "yarn build:background && yarn build:content && yarn build:popup",
"build:background": "vite --config vite.config.background.ts build",
"build:content": "vite --config vite.config.content.ts build",
"build:popup": "vite --config vite.config.popup.ts build",
"clean": "rimraf dist",
"copy:static": "copyfiles manifest.json assets/**/* dist",
"dist:dev": "yarn lint && yarn dist",
"dist": "yarn clean && yarn build && yarn copy:static"
"watch": "vite build --watch",
"build": "vite build"
},
"dependencies": {
"@emotion/react": "^11.11.4",
Expand All @@ -34,6 +24,7 @@
"react-router-dom": "^6.22.3"
},
"devDependencies": {
"@crxjs/vite-plugin": "2.0.0-beta.23",
"@eslint/js": "^8.57.0",
"@types/react": "^18.2.69",
"@types/react-dom": "^18.2.22",
Expand Down
2 changes: 1 addition & 1 deletion src/common/chrome-api-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class ChromeApiWrapper {

// Prevents code from failing when used too soon
while (Date.now() - dateStarted < 5000) {
const tabs = await chrome.tabs.query({active: true, lastFocusedWindow: true});
const tabs = await chrome.tabs.query({ active: true, lastFocusedWindow: true });

const currentTab = tabs[0];
if (currentTab?.status === 'complete') {
Expand Down
4 changes: 2 additions & 2 deletions src/content/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ChromeMessage, ChromeMessageType} from '../common/chrome-api-wrapper';
import {ScraperCommand, ScraperMessage} from '../common/types/scraper';
import { ChromeMessage, ChromeMessageType } from '../common/chrome-api-wrapper';
import { ScraperCommand, ScraperMessage } from '../common/types/scraper';

async function handleScrapeCommand() {
const pageTitle = document.title;
Expand Down
22 changes: 22 additions & 0 deletions src/popup/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { lazy, Suspense } from 'react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';

import AppShell from './AppShell/AppShell';
import PopupHeader from './components/PopupHeader/PopupHeader';

const HomePage = lazy(() => import('./features/HomePage/HomePage'));

export default function App() {
return (
<MemoryRouter>
<AppShell>
<Suspense fallback={<PopupHeader />}>
<Routes>
<Route path="/" element={<PopupHeader />} />
<Route path="/home-page" element={<HomePage />} />
</Routes>
</Suspense>
</AppShell>
</MemoryRouter>
);
}
6 changes: 3 additions & 3 deletions src/popup/AppShell/AppShell.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import './AppShell.css';

import {ReactElement, ReactNode, useEffect} from 'react';
import {useNavigate} from 'react-router-dom';
import { ReactElement, ReactNode, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

export default function AppShell(props: {children?: ReactNode}): ReactElement {
export default function AppShell(props: { children?: ReactNode }): ReactElement {
const navigate = useNavigate();

useEffect(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/popup/components/PopupContent/PopupContent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import './PopupContent.css';

import Box from '@mui/material/Box';
import {ReactElement, ReactNode} from 'react';
import { ReactElement, ReactNode } from 'react';

export default function PopupContent(props: {children?: ReactNode}): ReactElement {
export default function PopupContent(props: { children?: ReactNode }): ReactElement {
return (
<Box className="popup-content" display="flex" justifyContent="center" alignItems="center">
{props.children}
Expand Down
8 changes: 4 additions & 4 deletions src/popup/components/PopupHeader/PopupHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import './PopupHeader.css';

import ExtensionRoundedIcon from '@mui/icons-material/ExtensionRounded';
import {Toolbar} from '@mui/material';
import {ReactElement, ReactNode} from 'react';
import { Toolbar } from '@mui/material';
import { ReactElement, ReactNode } from 'react';

export default function PopupHeader(props: {children?: ReactNode}): ReactElement {
export default function PopupHeader(props: { children?: ReactNode }): ReactElement {
return (
<Toolbar className="popup-header" sx={{boxShadow: 1}}>
<Toolbar className="popup-header" sx={{ boxShadow: 1 }}>
<ExtensionRoundedIcon className="popup-logo" />
<h1>Chrome Extension React</h1>
{props.children}
Expand Down
10 changes: 5 additions & 5 deletions src/popup/features/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import './HomePage.css';

import {Alert, Button, Snackbar, Stack} from '@mui/material';
import { Alert, Button, Snackbar, Stack } from '@mui/material';
import Box from '@mui/material/Box';
import {ReactElement, useEffect, useState} from 'react';
import { ReactElement, useEffect, useState } from 'react';

import {
ChromeApiWrapper,
ChromeMessage,
ChromeMessageType
} from '../../../common/chrome-api-wrapper';
import {ScraperCommand, ScraperMessage} from '../../../common/types/scraper';
import { ScraperCommand, ScraperMessage } from '../../../common/types/scraper';
import PopupContent from '../../components/PopupContent/PopupContent';
import PopupHeader from '../../components/PopupHeader/PopupHeader';

Expand All @@ -31,7 +31,7 @@ export default function HomePage(): ReactElement {

const message: ChromeMessage<ScraperMessage> = {
type: ChromeMessageType.SCRAPER_COMMAND,
payload: {command: ScraperCommand.SCRAPE}
payload: { command: ScraperCommand.SCRAPE }
};

try {
Expand All @@ -55,7 +55,7 @@ export default function HomePage(): ReactElement {
return false;
}

chrome.storage.session.set({[CACHE_KEY]: message.payload});
chrome.storage.session.set({ [CACHE_KEY]: message.payload });
setScrapedPageTitle(message.payload);
setDisableScrapeButton(false);
return false;
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions index.html → src/popup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Chrome Extension React</title>

<link rel="stylesheet" href="src/popup/styles/index.css" />
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="src/popup/index.tsx"></script>
<script type="module" src="index.tsx"></script>
</body>
</html>
33 changes: 8 additions & 25 deletions src/popup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
import {lazy, ReactElement, StrictMode, Suspense} from 'react';
import {createRoot} from 'react-dom/client';
import {MemoryRouter, Route, Routes} from 'react-router-dom';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

import AppShell from './AppShell/AppShell';
import PopupHeader from './components/PopupHeader/PopupHeader';

const HomePage = lazy(() => import('./features/HomePage/HomePage'));

function Index(): ReactElement {
return (
<StrictMode>
<MemoryRouter>
<AppShell>
<Suspense fallback={<PopupHeader />}>
<Routes>
<Route path="/" element={<PopupHeader />} />
<Route path="/home-page" element={<HomePage />} />
</Routes>
</Suspense>
</AppShell>
</MemoryRouter>
</StrictMode>
);
}
import App from './App';

const root = createRoot(document.getElementById('root') as HTMLElement);
root.render(<Index />);
root.render(
<StrictMode>
<App />
</StrictMode>
);
7 changes: 1 addition & 6 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,5 @@
"jsx": "react-jsx",
"types": ["chrome-types"]
},
"include": [
"src",
"vite.config.popup.ts",
"vite.config.background.ts",
"vite.config.content.ts"
]
"include": ["src", "vite.config.ts", "vite.config.background.ts", "vite.config.content.ts"]
}
17 changes: 0 additions & 17 deletions vite.config.background.ts

This file was deleted.

17 changes: 0 additions & 17 deletions vite.config.content.ts

This file was deleted.

14 changes: 0 additions & 14 deletions vite.config.popup.ts

This file was deleted.

Loading

0 comments on commit 4ad893c

Please sign in to comment.