Skip to content

Commit

Permalink
Merge pull request #725
Browse files Browse the repository at this point in the history
* chore: create script for automatic creating `.env` file and update RE…

* chore: create script for load env from CLI into `.env`

* chore: create more validation for set_global_env.sh

* fix: remove unnecessary else from copy_env.sh

* chore: add `with-env` script to root package.json

* chore: update readme Env Vars section

* fix: remove type-check from build and remove cross-env where I've for…

* chore: add move explanation about CLI env setting to README.md

* chore: add node type for all modules tsconfig.json

* chore: add patterns for globalEnv for turbo

* chore: rename example env

* chore: update README.md for new approach

* chore: update set_global_env.sh script to the new prefixes CLI_CEB an…

* chore: update README.md for new approach

* chore: remove `with-env` and replace old env values

* chore: rename CI env to new pattern

* fix: change last forgotten __FIREFOX__ env

* chore: create base-dev script and remove unnecessary staff from `e2e`

* fix: add missing info for README.md

* fix: add missing CLI_CEB_* pattern for turbo.json

* fix: add missing(during conflicts resolving) `typescript` package to …

* fix: rename index.ts to .mts for index of `zipper` and adjust to new env

* feat: change CEB_DEV_LOCALE and isDev placeholders to real env values

* fix: define env file for i18n prepare-build

* fix: remove unnecessary noEmit from `chrome-extension` tsconfig

* clean: remove unnecessary isFirefox from manifest.ts

* fix: add missing "noEmit" for pre-build.tsconfig.json for chrome-exte…

* clean: remove unnecessary import from make-manifest-plugin.ts

* clean: remove unnecessary copy-env script from `set-global-env` script

* feat: create global env object and new solution for dynamic env values

* feat: update root README.md and move env part to env/README.md

* chore: add CEB_CI to example.env

* feat: change env module to be `module` (tsconfig)

* feat: changes refs from `process.env` to `env` from package

* chore: create base-build script and restart cli envs on build

* feat: create zipper as `module` (tsconfig) and add new env values

* fix: resolve pnpm-lock.yaml conflicts

* fix: remove unused `esbuild` package

* fix: issue with node packages polyfilling

* feat: restructure env module

* fix: adjust env call to new approach (with only process.env)

* fix: rename `consts` to `const` for `env` module

* docs: change README.md to solution with process.env

* clean: remove unnecessary values from wdio.browser.conf.ts
  • Loading branch information
PatrykKuniczak authored Jan 26, 2025
1 parent f6cfe9f commit 2b9f85e
Show file tree
Hide file tree
Showing 56 changed files with 1,254 additions and 200 deletions.
5 changes: 3 additions & 2 deletions .example.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
VITE_EXAMPLE=example
CEB_DEV_LOCALE=
CEB_EXAMPLE=example_env
CEB_DEV_LOCALE=
CEB_CI=
4 changes: 4 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ jobs:
chrome:
name: E2E tests for Chrome
runs-on: ubuntu-latest
env:
CEB_CI: true
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
Expand All @@ -23,6 +25,8 @@ jobs:
firefox:
name: E2E tests for Firefox
runs-on: ubuntu-latest
env:
CEB_CI: true
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
Expand Down
79 changes: 43 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
- [Install dependency](#install-dependency)
- [For root](#install-dependency-for-root)
- [For module](#install-dependency-for-module)
- [Environment variables](#env-variables)
- [Add new](#env-variables-new)
- [Set via CLI](#env-variables-cli-set)
- [Community](#community)
- [Reference](#reference)
- [Star History](#star-history)
Expand Down Expand Up @@ -70,12 +73,14 @@ the build speed and development experience by using Vite and Turborepo.
## Getting started

1. When you're using Windows run this:
- `git config --global core.eol lf`
- `git config --global core.autocrlf input`
- `git config --global core.eol lf`
- `git config --global core.autocrlf input`

**This will set the EOL (End of line) character to be the same as on Linux/macOS. Without this, our bash script won't work, and you will have conflicts with developers on Linux/macOS.**
**This will set the EOL (End of line) character to be the same as on Linux/macOS. Without this, our bash script won't
work, and you will have conflicts with developers on Linux/macOS.**
2. Clone this repository.( ```git clone https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite``` )
3. Check your node version is >= than in `.nvmrc` file, recommend to use [nvm](https://github.com/nvm-sh/nvm?tab=readme-ov-file#intro)
3. Check your node version is >= than in `.nvmrc` file, recommend to
use [nvm](https://github.com/nvm-sh/nvm?tab=readme-ov-file#intro)
4. Edit `/packages/i18n/locales/`{your locale(s)}/`messages.json`
5. In the objects `extensionDescription` and `extensionName`, change the `message` fields (leave `description` alone)
6. In `/.package.json`, change the `version` to the desired version of your extension.
Expand All @@ -87,7 +92,8 @@ Then, depending on the target browser:
### For Chrome: <a name="getting-started-chrome"></a>

1. Run:
- Dev: `pnpm dev` (on Windows, you should run as administrator; see [issue#456](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/456))
- Dev: `pnpm dev` (on Windows, you should run as administrator;
see [issue#456](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/456))
- Prod: `pnpm build`
2. Open in browser - `chrome://extensions`
3. Check - <kbd>Developer mode</kbd>
Expand All @@ -104,7 +110,8 @@ Then, depending on the target browser:
4. Select the `./dist/manifest.json` file from the boilerplate project

> [!NOTE]
> In Firefox, you load add-ons in temporary mode. That means they'll disappear after each browser close. You have to load the add-on on every browser launch.
> In Firefox, you load add-ons in temporary mode. That means they'll disappear after each browser close. You have to
> load the add-on on every browser launch.
## Install dependency for turborepo: <a name="install-dependency"></a>

Expand All @@ -117,26 +124,12 @@ Then, depending on the target browser:
1. Run `pnpm i <package> -F <module name>`

`package` - Name of the package you want to install e.g. `nodemon` \
`module-name` - You can find it inside each `package.json` under the key `name`, e.g. `@extension/content-script`, you can use only `content-script` without `@extension/` prefix
`module-name` - You can find it inside each `package.json` under the key `name`, e.g. `@extension/content-script`, you
can use only `content-script` without `@extension/` prefix

## Environment variables

To add an environment variable:

1. Copy `.example.env` to `.env` (in the same directory)
2. Add a new record inside `.env`, prefixed with `VITE_`, e.g. `VITE_MY_API_KEY=...`
3. Edit `./vite-env.d.ts` and in the `ImportMetaEnv` interface, add your variable with the appropriate type, e.g.

`readonly VITE_MY_API_KEY: string;`
4. Then you can read the variable via `import.meta.env.VITE_MY_API_KEY` (learn more at [Env Variables and Modes](https://vite.dev/guide/env-and-mode))

#### If you want to set it for each package independently:

1. Create `.env` inside that package
2. Open related `vite.config.mts` and add `envDir: '.'` at the end of this config
3. Rest steps like above

#### Remember you can't use global and local at the same time for the same package(It will be overwritten)
Read: [Env Documentation](packages/env/README.md)

## Boilerplate structure <a name="structure"></a>

Expand All @@ -145,7 +138,8 @@ To add an environment variable:
The extension lives in the `chrome-extension` directory and includes the following files:

- [`manifest.ts`](chrome-extension/manifest.js) - script that outputs the `manifest.json`
- [`src/background`](chrome-extension/src/background) - [background script](https://developer.chrome.com/docs/extensions/mv3/background_pages/)
- [
`src/background`](chrome-extension/src/background) - [background script](https://developer.chrome.com/docs/extensions/mv3/background_pages/)
(`background.service_worker` in manifest.json)
- [`public`](chrome-extension/public/) - icons referenced in the manifest; content CSS for user's page injection

Expand All @@ -159,39 +153,50 @@ The extension lives in the `chrome-extension` directory and includes the followi

Code that is transpiled to be part of the extension lives in the [pages](pages/) directory.

- [`content`](pages/content/) - [content scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts)
- [
`content`](pages/content/) - [content scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts)
(`content_scripts` in manifest.json)
- [`content-ui`](pages/content-ui) - React UI rendered in the current page (you can see it at the very bottom when you get started)
- [`content-ui`](pages/content-ui) - React UI rendered in the current page (you can see it at the very bottom when you
get started)
(`content_scripts` in manifest.json)
- [`content-runtime`](pages/content-runtime/src/) - [injected content scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#functionality);
- [
`content-runtime`](pages/content-runtime/src/) - [injected content scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#functionality);
this can be injected from `popup` like standard `content`
- [`devtools`](pages/devtools/) - [extend the browser DevTools](https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools#creating)
- [
`devtools`](pages/devtools/) - [extend the browser DevTools](https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools#creating)
(`devtools_page` in manifest.json)
- [`devtools-panel`](pages/devtools-panel/) - [DevTools panel](https://developer.chrome.com/docs/extensions/reference/api/devtools/panels)
- [
`devtools-panel`](pages/devtools-panel/) - [DevTools panel](https://developer.chrome.com/docs/extensions/reference/api/devtools/panels)
for [devtools](pages/devtools/src/index.ts)
- [`new-tab`](pages/new-tab/) - [override the default New Tab page](https://developer.chrome.com/docs/extensions/develop/ui/override-chrome-pages)
- [
`new-tab`](pages/new-tab/) - [override the default New Tab page](https://developer.chrome.com/docs/extensions/develop/ui/override-chrome-pages)
(`chrome_url_overrides.newtab` in manifest.json)
- [`options`](pages/options/) - [options page](https://developer.chrome.com/docs/extensions/develop/ui/options-page)
(`options_page` in manifest.json)
- [`popup`](pages/popup/) - [popup](https://developer.chrome.com/docs/extensions/reference/api/action#popup) shown when clicking the extension in the toolbar
- [`popup`](pages/popup/) - [popup](https://developer.chrome.com/docs/extensions/reference/api/action#popup) shown when
clicking the extension in the toolbar
(`action.default_popup` in manifest.json)
- [`side-panel`](pages/side-panel/) - [sidepanel (Chrome 114+)](https://developer.chrome.com/docs/extensions/reference/api/sidePanel)
- [
`side-panel`](pages/side-panel/) - [sidepanel (Chrome 114+)](https://developer.chrome.com/docs/extensions/reference/api/sidePanel)
(`side_panel.default_path` in manifest.json)

### Packages <a name="structure-packages"></a>

Some shared packages:

- `dev-utils` - utilities for Chrome extension development (manifest-parser, logger)
- `i18n` - custom internationalization package; provides i18n function with type safety and other validation
- `env` - exports object which contain all environment variables from `.env` and dynamically declared
- `hmr` - custom HMR plugin for Vite, injection script for reload/refresh, HMR dev-server
- `i18n` - custom internationalization package; provides i18n function with type safety and other validation
- `shared` - shared code for the entire project (types, constants, custom hooks, components etc.)
- `storage` - helpers for easier integration with [storage](https://developer.chrome.com/docs/extensions/reference/api/storage), e.g. local/session storages
- `storage` - helpers for easier integration
with [storage](https://developer.chrome.com/docs/extensions/reference/api/storage), e.g. local/session storages
- `tailwind-config` - shared Tailwind config for entire project
- `tsconfig` - shared tsconfig for the entire project
- `ui` - function to merge your Tailwind config with the global one; you can save components here
- `vite-config` - shared Vite config for the entire project
- `zipper` - run `pnpm zip` to pack the `dist` folder into `extension-YYYYMMDD-HHmmss.zip` inside the newly created `dist-zip`
- `zipper` - run `pnpm zip` to pack the `dist` folder into `extension-YYYYMMDD-HHmmss.zip` inside the newly created
`dist-zip`
- `e2e` - run `pnpm e2e` for end-to-end tests of your zipped extension on different browsers

## Troubleshooting
Expand All @@ -202,7 +207,9 @@ If saving source files doesn't cause the extension HMR code to trigger a reload

1. Ctrl+C the development server and restart it (`pnpm run dev`)
2. If you get a [`grpc` error](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/612),
[kill the `turbo` process](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/612#issuecomment-2518982339) and run `pnpm dev` again.
[kill the
`turbo` process](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/612#issuecomment-2518982339)
and run `pnpm dev` again.

## Community

Expand Down
9 changes: 5 additions & 4 deletions chrome-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@
"clean": "pnpm clean:turbo && pnpm clean:node_modules",
"ready": "tsc -b pre-build.tsconfig.json",
"build": "vite build",
"dev": "cross-env __DEV__=true vite build --mode development",
"dev": "vite build --mode development",
"test": "vitest run",
"lint": "eslint ./ --ext .ts,.js,.tsx,.jsx",
"lint:fix": "pnpm lint --fix",
"prettier": "prettier . --write --ignore-path ../.prettierignore",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@extension/env": "workspace:*",
"@extension/shared": "workspace:*",
"@extension/storage": "workspace:*",
"vite-plugin-node-polyfills": "^0.23.0",
"webextension-polyfill": "^0.12.0"
},
"devDependencies": {
Expand All @@ -30,9 +32,8 @@
"@extension/vite-config": "workspace:*",
"@laynezh/vite-plugin-lib-assets": "^0.6.1",
"@types/ws": "^8.5.13",
"deepmerge": "^4.3.1",
"magic-string": "^0.30.10",
"ts-loader": "^9.5.1",
"cross-env": "^7.0.3",
"deepmerge": "^4.3.1"
"ts-loader": "^9.5.1"
}
}
11 changes: 5 additions & 6 deletions chrome-extension/utils/plugins/make-manifest-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
import { env, platform } from 'node:process';
import { platform } from 'node:process';
import type { Manifest } from '@extension/dev-utils';
import { colorLog, ManifestParser } from '@extension/dev-utils';
import type { PluginOption } from 'vite';
import { IS_DEV, IS_FIREFOX } from '@extension/env';

const manifestFile = resolve(import.meta.dirname, '..', '..', 'manifest.js');
const refreshFilePath = resolve(
Expand Down Expand Up @@ -45,16 +46,14 @@ export default (config: { outDir: string }): PluginOption => {
}

const manifestPath = resolve(to, 'manifest.json');
const isFirefox = env.__FIREFOX__ === 'true';
const isDev = env.__DEV__ === 'true';

isDev && addRefreshContentScript(manifest);
IS_DEV && addRefreshContentScript(manifest);

writeFileSync(manifestPath, ManifestParser.convertManifestToString(manifest, isFirefox));
writeFileSync(manifestPath, ManifestParser.convertManifestToString(manifest, IS_FIREFOX));

const refreshFileString = readFileSync(refreshFilePath, 'utf-8');

isDev && writeFileSync(resolve(to, 'refresh.js'), withHMRId(refreshFileString));
IS_DEV && writeFileSync(resolve(to, 'refresh.js'), withHMRId(refreshFileString));

colorLog(`Manifest file copy complete: ${manifestPath}`, 'success');
};
Expand Down
17 changes: 11 additions & 6 deletions chrome-extension/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import { defineConfig, type PluginOption } from 'vite';
import libAssetsPlugin from '@laynezh/vite-plugin-lib-assets';
import makeManifestPlugin from './utils/plugins/make-manifest-plugin.js';
import { watchPublicPlugin, watchRebuildPlugin } from '@extension/hmr';
import { isDev, isProduction, watchOption } from '@extension/vite-config';
import { watchOption } from '@extension/vite-config';
import env, { IS_DEV, IS_PROD } from '@extension/env';
import { nodePolyfills } from 'vite-plugin-node-polyfills';

const rootDir = resolve(import.meta.dirname);
const srcDir = resolve(rootDir, 'src');

const outDir = resolve(rootDir, '..', 'dist');
export default defineConfig({
define: {
'process.env': env,
},
resolve: {
alias: {
'@root': rootDir,
Expand All @@ -23,7 +28,8 @@ export default defineConfig({
}) as PluginOption,
watchPublicPlugin(),
makeManifestPlugin({ outDir }),
isDev && watchRebuildPlugin({ reload: true, id: 'chrome-extension-hmr' }),
IS_DEV && watchRebuildPlugin({ reload: true, id: 'chrome-extension-hmr' }),
nodePolyfills(),
],
publicDir: resolve(rootDir, 'public'),
build: {
Expand All @@ -35,13 +41,12 @@ export default defineConfig({
},
outDir,
emptyOutDir: false,
sourcemap: isDev,
minify: isProduction,
reportCompressedSize: isProduction,
sourcemap: IS_DEV,
minify: IS_PROD,
reportCompressedSize: IS_PROD,
watch: watchOption,
rollupOptions: {
external: ['chrome'],
},
},
envDir: '../',
});
8 changes: 8 additions & 0 deletions copy_env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

# Check if .env does not exist and .example.env exists
if [ ! -f ".env" ] && [ -f ".example.env" ]; then
# Copy .example.env to .env
cp .example.env .env
echo ".example.env has been copied to .env"
fi
26 changes: 15 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,26 @@
"clean:turbo": "rimraf .turbo && turbo clean:turbo",
"clean": "pnpm clean:bundle && pnpm clean:turbo && pnpm clean:node_modules",
"clean:install": "pnpm clean:node_modules && pnpm install --frozen-lockfile",
"build": "pnpm clean:bundle && turbo ready && turbo build",
"build:firefox": "pnpm clean:bundle && turbo ready && cross-env __FIREFOX__=true turbo build",
"zip": "pnpm build && pnpm -F zipper zip",
"zip:firefox": "pnpm build:firefox && cross-env __FIREFOX__=true pnpm -F zipper zip",
"dev": "turbo ready && cross-env __DEV__=true turbo watch dev --concurrency 20",
"dev:firefox": "turbo ready && cross-env __DEV__=true __FIREFOX__=true turbo watch dev --concurrency 20",
"e2e": "pnpm build && pnpm zip && turbo e2e",
"e2e:firefox": "pnpm build:firefox && pnpm zip:firefox && cross-env __FIREFOX__=true turbo e2e",
"type-check": "turbo type-check",
"ready": "turbo ready",
"base-build": "pnpm clean:bundle && pnpm ready && turbo build",
"build": "pnpm set-global-env && pnpm base-build",
"build:firefox": "pnpm set-global-env CLI_CEB_FIREFOX=true && pnpm base-build",
"base-dev": "pnpm clean:bundle && pnpm ready && turbo watch dev --concurrency 20",
"dev": "pnpm set-global-env CLI_CEB_DEV=true && pnpm base-dev",
"dev:firefox": "pnpm set-global-env CLI_CEB_DEV=true CLI_CEB_FIREFOX=true && pnpm base-dev",
"zip": "pnpm build && pnpm -F zipper zip",
"zip:firefox": "pnpm build:firefox && pnpm -F zipper zip",
"e2e": "pnpm zip && turbo e2e",
"e2e:firefox": "pnpm zip:firefox && turbo e2e",
"lint": "turbo lint --continue -- --fix --cache --cache-location node_modules/.cache/.eslintcache",
"lint:fix": "turbo lint:fix --continue -- --fix --cache --cache-location node_modules/.cache/.eslintcache",
"prettier": "turbo prettier --continue -- --cache --cache-location node_modules/.cache/.prettiercache",
"prepare": "husky",
"update-version": "bash update_version.sh"
"update-version": "bash update_version.sh",
"copy_env": "bash copy_env.sh",
"set-global-env": "bash set_global_env.sh",
"postinstall": "pnpm copy_env"
},
"dependencies": {
"eslint-plugin-tailwindcss": "^3.17.4",
Expand All @@ -45,9 +51,7 @@
"@typescript-eslint/parser": "^7.18.0",
"app-root-path": "^3.1.0",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"deepmerge": "^4.3.1",
"esbuild": "^0.23.0",
"eslint": "8.57.0",
"eslint-config-airbnb-typescript": "18.0.0",
"eslint-config-prettier": "9.1.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/env/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
31 changes: 31 additions & 0 deletions packages/env/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Environment Package

This package contains code which creates env values.
To use the code in the package, you need to follow those steps:

1. Add a new record to `.env` (NEED TO CONTAIN `CEB_` PREFIX),

- If you want via cli:
- Add it as argument like: `pnpm set-global-env CLI_CEB_NEXT_VALUE=new_data ...` (NEED TO CONTAIN `CLI_CEB_` PREFIX)

> [!IMPORTANT]
> `CLI_CEB_DEV` and `CLI_CEB_FIREFOX` are `false` by default \
> All CLI values are overwriting in each call, that's mean you'll have access to values from current script run only.
- If you want dynamic variables go to `lib/index.ts` and edit `dynamicEnvValues` object.

2. Use it, for example:
```ts
console.log(process.env['CEB_EXAMPLE']);
```
or
```ts
console.log(process.env.CEB_EXAMPLE);
```
but with first solution, autofill should work for IDE:
![img.png](img.png)
3. You are also able to import const like `IS_DEV` from `@extension/env` like:
```ts
import { IS_DEV } from '@extension/env';
```
For more look [ENV CONST](lib/const.ts)
Binary file added packages/env/img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2b9f85e

Please sign in to comment.