Full Code of PlasmoHQ/plasmo for AI

main 9369e2835de2 cached
334 files
506.4 KB
145.3k tokens
376 symbols
1 requests
Download .txt
Showing preview only (580K chars total). Download the full file or copy to clipboard to get everything.
Repository: PlasmoHQ/plasmo
Branch: main
Commit: 9369e2835de2
Files: 334
Total size: 506.4 KB

Directory structure:
gitextract_fsxmfk6d/

├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 0.rfc.yml
│   │   ├── 1.bug.yml
│   │   ├── 2.example.yml
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── SECURITY.md
│   └── workflows/
│       ├── test-examples.yml
│       └── test-package.yml
├── .gitignore
├── .gitmodules
├── .npmrc
├── .prettierrc.mjs
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── LICENSE
├── api/
│   ├── messaging/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── jest.config.mjs
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── background.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   ├── message.ts
│   │   │   ├── port.ts
│   │   │   ├── pub-sub.ts
│   │   │   ├── relay.test.ts
│   │   │   ├── relay.ts
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   └── tsconfig.json
│   ├── persistent/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── background.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   └── selector/
│       ├── .gitignore
│       ├── LICENSE
│       ├── package.json
│       ├── src/
│       │   ├── background.ts
│       │   ├── hook.ts
│       │   ├── index.ts
│       │   ├── monitor.ts
│       │   └── types.ts
│       ├── tsconfig.json
│       └── tsup.config.ts
├── cli/
│   ├── create-plasmo/
│   │   ├── bin/
│   │   │   └── index.mjs
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── commands.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   └── plasmo/
│       ├── .eslintrc.js
│       ├── LICENSE
│       ├── README.md
│       ├── bin/
│       │   └── index.mjs
│       ├── i18n/
│       │   ├── README.de-DE.md
│       │   ├── README.fr-FR.md
│       │   ├── README.id-ID.md
│       │   ├── README.ja-JP.md
│       │   ├── README.ko-KR.md
│       │   ├── README.ru-RU.md
│       │   ├── README.tr-TR.md
│       │   ├── README.vi-VN.md
│       │   └── README.zh-CN.md
│       ├── index.mjs
│       ├── package.json
│       ├── src/
│       │   ├── commands/
│       │   │   ├── build.ts
│       │   │   ├── dev.ts
│       │   │   ├── help.ts
│       │   │   ├── index.ts
│       │   │   ├── init.ts
│       │   │   ├── package.ts
│       │   │   ├── start.ts
│       │   │   └── version.ts
│       │   ├── features/
│       │   │   ├── background-service-worker/
│       │   │   │   ├── bgsw-entry.ts
│       │   │   │   ├── bgsw-main-world-script.ts
│       │   │   │   ├── bgsw-messaging-declaration.ts
│       │   │   │   ├── bgsw-messaging.ts
│       │   │   │   └── update-bgsw-entry.ts
│       │   │   ├── env/
│       │   │   │   ├── env-config.ts
│       │   │   │   └── env-declaration.ts
│       │   │   ├── extension-devtools/
│       │   │   │   ├── common-path.ts
│       │   │   │   ├── content-script-config.ts
│       │   │   │   ├── generate-icons.ts
│       │   │   │   ├── get-bundle-config.ts
│       │   │   │   ├── git-ignore.ts
│       │   │   │   ├── package-file.ts
│       │   │   │   ├── parse-ast.ts
│       │   │   │   ├── project-path.ts
│       │   │   │   ├── project-watcher.ts
│       │   │   │   ├── strip-underscore.ts
│       │   │   │   ├── template-path.ts
│       │   │   │   └── tsconfig.ts
│       │   │   ├── extra/
│       │   │   │   ├── cache-busting.ts
│       │   │   │   └── next-new-tab.ts
│       │   │   ├── framework-update/
│       │   │   │   └── version-tracker.ts
│       │   │   ├── helpers/
│       │   │   │   ├── create-parcel-bundler.ts
│       │   │   │   ├── crypto.ts
│       │   │   │   ├── flag.ts
│       │   │   │   ├── loading-animation.ts
│       │   │   │   ├── package-manager.ts
│       │   │   │   ├── print.ts
│       │   │   │   ├── prompt.ts
│       │   │   │   └── traverse.ts
│       │   │   ├── manifest-factory/
│       │   │   │   ├── base.ts
│       │   │   │   ├── create-manifest.ts
│       │   │   │   ├── mv2.ts
│       │   │   │   ├── mv3.ts
│       │   │   │   ├── scaffolder.ts
│       │   │   │   ├── ui-library.ts
│       │   │   │   └── zip.ts
│       │   │   └── project-creator/
│       │   │       ├── from-existing-manifest.ts
│       │   │       ├── get-raw-name.ts
│       │   │       ├── git-init.ts
│       │   │       ├── index.ts
│       │   │       ├── install-dependencies.ts
│       │   │       └── print-ready.ts
│       │   ├── index.ts
│       │   └── type.ts
│       ├── templates/
│       │   ├── plasmo.d.ts
│       │   ├── static/
│       │   │   ├── background/
│       │   │   │   └── index.ts
│       │   │   ├── common/
│       │   │   │   ├── csui-container-react.tsx
│       │   │   │   ├── csui-container-vanilla.tsx
│       │   │   │   ├── csui.ts
│       │   │   │   ├── react.ts
│       │   │   │   └── vue.ts
│       │   │   ├── react17/
│       │   │   │   ├── content-script-ui-mount.tsx
│       │   │   │   ├── index.html
│       │   │   │   └── index.tsx
│       │   │   ├── react18/
│       │   │   │   ├── content-script-ui-mount.tsx
│       │   │   │   ├── index.html
│       │   │   │   └── index.tsx
│       │   │   ├── react19/
│       │   │   │   ├── content-script-ui-mount.tsx
│       │   │   │   ├── index.html
│       │   │   │   └── index.tsx
│       │   │   ├── svelte4/
│       │   │   │   ├── content-script-ui-mount.ts
│       │   │   │   ├── index.html
│       │   │   │   └── index.ts
│       │   │   ├── vanilla/
│       │   │   │   ├── index.html
│       │   │   │   └── index.ts
│       │   │   └── vue3/
│       │   │       ├── content-script-ui-mount.ts
│       │   │       ├── index.html
│       │   │       └── index.ts
│       │   └── tsconfig.base.json
│       └── tsconfig.json
├── core/
│   ├── parcel-bundler/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── bit-set.ts
│   │   │   ├── can-merge.ts
│   │   │   ├── create-bundle.ts
│   │   │   ├── create-ideal-graph.ts
│   │   │   ├── decorate-legacy-graph.ts
│   │   │   ├── get-entry-by-target.ts
│   │   │   ├── get-reachable-bundle-root.ts
│   │   │   ├── index.ts
│   │   │   ├── remove-bundle.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── parcel-compressor-utf8/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   └── utf8-transform.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-config/
│   │   ├── .gitignore
│   │   ├── index.json
│   │   └── package.json
│   ├── parcel-core/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── resolve-options.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-namer-manifest/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── parcel-optimizer-encapsulate/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-optimizer-es/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── blob-to-string.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-packager/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── get-web-accessible-resources.ts
│   │   │   ├── index.ts
│   │   │   └── utils.ts
│   │   └── tsconfig.json
│   ├── parcel-resolver/
│   │   ├── .gitignore
│   │   ├── index.mjs
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── dev-polyfills/
│   │   │   │   ├── react-refresh/
│   │   │   │   │   └── runtime.ts
│   │   │   │   └── react-refresh.ts
│   │   │   ├── handle-absolute-root.ts
│   │   │   ├── handle-alias.ts
│   │   │   ├── handle-plasmo-internal.ts
│   │   │   ├── handle-polyfill.ts
│   │   │   ├── handle-remote-caching.ts
│   │   │   ├── handle-tilde-src.ts
│   │   │   ├── index.ts
│   │   │   ├── polyfills/
│   │   │   │   ├── assert.ts
│   │   │   │   ├── buffer.ts
│   │   │   │   ├── console.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── crc-32/
│   │   │   │   │   └── crc32c.ts
│   │   │   │   ├── crc-32.ts
│   │   │   │   ├── crypto.ts
│   │   │   │   ├── domain.ts
│   │   │   │   ├── events.ts
│   │   │   │   ├── http.ts
│   │   │   │   ├── https.ts
│   │   │   │   ├── os.ts
│   │   │   │   ├── path.ts
│   │   │   │   ├── process.ts
│   │   │   │   ├── punycode.ts
│   │   │   │   ├── querystring.ts
│   │   │   │   ├── stream.ts
│   │   │   │   ├── string_decoder.ts
│   │   │   │   ├── sys.ts
│   │   │   │   ├── timers.ts
│   │   │   │   ├── tty.ts
│   │   │   │   ├── url.ts
│   │   │   │   ├── util.ts
│   │   │   │   ├── vm.ts
│   │   │   │   └── zlib.ts
│   │   │   └── shared.ts
│   │   └── tsconfig.json
│   ├── parcel-resolver-post/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── handle-hacks.ts
│   │   │   ├── handle-module-exports.ts
│   │   │   ├── handle-ts-path.ts
│   │   │   ├── index.ts
│   │   │   ├── shared.ts
│   │   │   └── utils.ts
│   │   └── tsconfig.json
│   ├── parcel-runtime/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── runtimes/
│   │   │   │   ├── background-service-runtime.ts
│   │   │   │   ├── page-runtime.ts
│   │   │   │   └── script-runtime.ts
│   │   │   ├── types.ts
│   │   │   └── utils/
│   │   │       ├── 0-patch-module.ts
│   │   │       ├── bgsw.ts
│   │   │       ├── hmr-check.ts
│   │   │       ├── hmr-utils.ts
│   │   │       ├── inject-socket.ts
│   │   │       ├── loading-indicator.ts
│   │   │       └── react-refresh.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-transformer-inject-env/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-inline-css/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── get-tagets.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-lab/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   └── state.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-manifest/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── runtime/
│   │   │   └── plasmo-default-background.ts
│   │   ├── src/
│   │   │   ├── csp-patch-hmr.ts
│   │   │   ├── handle-action.ts
│   │   │   ├── handle-background.ts
│   │   │   ├── handle-content-scripts.ts
│   │   │   ├── handle-declarative-net-request.ts
│   │   │   ├── handle-deep-loc.ts
│   │   │   ├── handle-dictionaries.ts
│   │   │   ├── handle-locales.ts
│   │   │   ├── handle-sandboxes.ts
│   │   │   ├── handle-tabs.ts
│   │   │   ├── index.ts
│   │   │   ├── normalize-manifest.ts
│   │   │   ├── schema.ts
│   │   │   ├── state.ts
│   │   │   ├── utils.ts
│   │   │   └── validate-version.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-svelte/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── convert-error.ts
│   │   │   ├── convert-loc.ts
│   │   │   ├── index.ts
│   │   │   ├── source-map.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   └── parcel-transformer-vue/
│       ├── .gitignore
│       ├── package.json
│       ├── src/
│       │   └── index.ts
│       └── tsconfig.json
├── eslint.config.mjs
├── package.json
├── packages/
│   ├── framework-shared/
│   │   ├── build-socket/
│   │   │   ├── event.ts
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   └── init/
│       ├── .gitignore
│       ├── bpp.yml
│       ├── entries/
│       │   ├── background.ts
│       │   ├── content.ts
│       │   ├── contents/
│       │   │   ├── inline.tsx
│       │   │   └── overlay.tsx
│       │   ├── newtab.tsx
│       │   ├── options.tsx
│       │   └── popup.tsx
│       ├── index.json
│       ├── package.json
│       ├── templates/
│       │   ├── README.md
│       │   └── tsconfig.json
│       └── tsconfig.json
├── pnpm-workspace.yaml
├── renovate.json
├── scripts/
│   └── move-prettier-cjs-to-mjs.bash
└── turbo.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
## Code of Conduct

### Our Pledge

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

### Our Standards

Examples of behavior that contributes to a positive environment for our community include:

- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community

Examples of unacceptable behavior include:

- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others’ private information, such as a physical or email address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting

### Enforcement Responsibilities

Project maintainers are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.

### Scope

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

### Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project team responsible for enforcement at [foss@plasmo.com](mailto:foss@plasmo.com). All complaints will be reviewed and investigated promptly and fairly.

All project maintainers are obligated to respect the privacy and security of the reporter of any incident.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

### Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1,
available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct/][version]

[homepage]: http://contributor-covenant.org
[version]: https://www.contributor-covenant.org/version/2/1


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to Plasmo

To contribute to [our examples](https://github.com/PlasmoHQ/examples/), please see **[Adding examples](#adding-examples)** below.

## Contributing

1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it:

   ```bash
   git clone git@github.com:<org>/plasmo.git --recurse-submodules
   ```

   **NOTE:** Replace `<org>` with your GitHub username or organization.

1. Work on your fork's `main` branch, then [open a PR](https://github.com/PlasmoHQ/plasmo/compare). Please ensure the PR name follows the naming convention:

   `feat: some new feature`

   Replacing `feat` with `fix`, `bug` or `doc` accordingly

## Adding examples

When you add an example to the [examples](https://github.com/PlasmoHQ/examples/) repository:

- Use `pnpm create plasmo --exp` to create the example.
- The name of the example should have a `with-*` prefix.
- To add additional notes, add a `## Notes` section at the start of the generated readme.
- Your PR should be pointed to the [examples project](https://github.com/PlasmoHQ/examples/).

## Developing

The development branch is `main`, and this is the branch that all pull
requests should be made against.

To develop locally:

1. Install [pnpm](https://pnpm.io/)
   - DO NOT install pnpm as a global npm dependency, we need pnpm to be linked directly to your $PATH.
   - Recommended installation method is with corepack or with brew (on macOS)
   - If installed with brew, you might need to include the pnpm $PATH to your debugger
2. Install the dependencies with:

   ```
   pnpm i
   ```

3. Start developing and watch for code changes:

   ```
   pnpm dev:cli
   ```

## Developing with your local version of Plasmo

### As global link

1. Link `plasmo` to your local registry:

   ```sh
   cd plasmo/cli/plasmo
   pnpm link --global
   ```

2. Invoke plasmo directly:

   ```sh
   plasmo init
   plasmo dev
   plasmo build
   ```

3. To revert the linking later on:

   ```sh
   pnpm rm -g plasmo
   ```

Note: The `create-plasmo` CLI tool is not meant to be run locally.
If you have already linked it, please run

```sh
pnpm -g unlink create-plasmo
```

to unlink it.

## Building

You can build the project, including all type definitions, with:

```bash
pnpm build
```

## Naming convention

### Files and directories

Any files that require attention for reading should be `UPPER_CASE`. Examples:

- README.md
- LICENSE
- SECURITY.md
- CONTRIBUTING.md

Directory and source file should use `kebab-case`, unless required by tooling. Examples:

- cli/plasmo/src/features/extension-devtools/plasmo-extension-manifest.ts

### Code

| Concept              | Naming convention       |
| -------------------- | ----------------------- |
| Local constants      | `UPPER_CASE`            |
| Enum namespace       | `PascalCase`            |
| Enum values          | `PascalCase`            |
| TS types             | `PascalCase`            |
| TS fields            | `camelCase`             |
| React component      | `PascalCase`            |
| React hook           | `camelCase`             |
| Local variable       | `camelCase`             |
| Unused argument      | `_paddedCamelCase`      |
| Template Placeholder | `__snake_case_padded__` |
| Functions            | `camelCase`             |
| API Routes           | `kebab-case`            |

## For Core Maintainers / Admin

Plasmo has 2 deployed environments:

| env name | purpose        | requirement           |
| -------- | -------------- | --------------------- |
| lab      | For WIP test   | Admin deploy directly |
| latest   | Stable release | Merge to `stable`     |

Reviewer approves and merges PRs to `main` branch -> deploys to `latest`

> NOTE: Please make sure to use the `Squash and Merge` strategy

For `hotfix`, the workflow is:

1. Creates a `hotfix-FFFF` branch off of `stable` and a PR to `stable`

   ```sh
   git checkout stable
   git checkout -b hotfix-FFFF
   ```

   PR name: `hotfix: some quick patch`

   `FFFF` is an issue number

1. Admin reviews, approves and merges `hotfix-FFFF` to `main` -> deploys to `latest`

### Merge strategy

1. Admin review PR
1. If the rough idea is good, code owner season the PR or guide the author to make it better
1. Merge and deploy following the table below:

| From       | To     | Strategy         | Deploy to |
| ---------- | ------ | ---------------- | --------- |
| `feat-*`   | `main` | Squash and Merge | latest    |
| `hotfix-*` | `main` | Squash and Merge | latest    |


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: PlasmoHQ

# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .github/ISSUE_TEMPLATE/0.rfc.yml
================================================
name: ⚡ Request for Comments
description: File an RFC for Feature Request/Enhancement/Refactor
title: "[RFC] "
labels: ["enhancement"]
body:
  - type: markdown
    attributes:
      value: |
        **Thank you for taking the time to fill out this RFC!** 🥳

  - type: textarea
    id: description
    attributes:
      label: How do you envision this feature/change to look/work like?
      description: Please be as detailed as possible, providing any relevant context.
      placeholder: The framework should read icon-1024.png and transforms them into smaller icons...
    validations:
      required: true

  - type: textarea
    id: purpose
    attributes:
      label: What is the purpose of this change/feature? Why?
      description: Please provide a simple summary/abstraction.
      placeholder: |
        The current image is not versatile enough, i.e it is too small.
    validations:
      required: true

  - type: textarea
    id: examples
    attributes:
      label: (OPTIONAL) Example implementations
      description: If you have any examples of how this feature/change works, please list them here.
    validations:
      required: false

  - type: checkboxes
    id: contribution
    attributes:
      label: (OPTIONAL) Contribution
      description: Would you be willing to create a PR to solve this issue?
      options:
        - label: I would like to contribute to this RFC via a PR
          required: false

  - type: checkboxes
    attributes:
      label: Verify canary release
      description: "`plasmo@canary` is the canary version of Plasmo framework that ships daily. It includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue."
      options:
        - label: I verified that the issue exists in `plasmo` canary release
          required: true

  - type: checkboxes
    id: terms
    attributes:
      label: Code of Conduct
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md).
      options:
        - label: I agree to follow this project's Code of Conduct
          required: true
        - label: I checked the [current issues](https://github.com/PlasmoHQ/plasmo/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement+) for duplicate problems.
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/1.bug.yml
================================================
name: 🐛 Bug Report
description: File a bug report
title: "[BUG] "
labels: ["bug", "triage"]
body:
  - type: markdown
    attributes:
      value: |
        **Thank you for taking the time to fill out this bug report!** 🥳

  - type: textarea
    id: what-happened
    attributes:
      label: What happened?
      description: Also tell us, what did you expect to happen?
      placeholder: Tell us what you see!
      value: "A bug happened!"
    validations:
      required: true

  # - type: checkboxes
  #   attributes:
  #     label: Verify canary release
  #     description: '`plasmo@canary` is the canary version of Plasmo framework that ships daily. It includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue.'
  #     options:
  #       - label: I verified that the issue exists in `plasmo` canary release
  #         required: true

  - type: dropdown
    id: version
    attributes:
      label: Version
      description: What version of the framework are you using?
      options:
        - Latest
        - Canary
    validations:
      required: true

  - type: dropdown
    id: operating-system
    attributes:
      label: What OS are you seeing the problem on?
      multiple: true
      options:
        - Windows
        - MacOSX
        - Linux
        - Other

  - type: dropdown
    id: browsers
    attributes:
      label: What browsers are you seeing the problem on?
      multiple: true
      options:
        - Chrome
        - Microsoft Edge
        - Opera
        - Safari
        - Firefox

  - type: textarea
    id: logs
    attributes:
      label: Relevant log output
      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
      render: Shell

  - type: checkboxes
    id: contribution
    attributes:
      label: (OPTIONAL) Contribution
      description: Would you be willing to create a PR to solve this issue?
      options:
        - label: I would like to fix this BUG via a PR
          required: false

  - type: checkboxes
    id: terms
    attributes:
      label: Code of Conduct
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md).
      options:
        - label: I agree to follow this project's Code of Conduct
          required: true
        - label: I checked the [current issues](https://github.com/PlasmoHQ/plasmo/issues?q=is%3Aopen+is%3Aissue+label%3Abug) for duplicate problems.
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/2.example.yml
================================================
name: 📓 Request/Improve an Example
description: Request or Improve a Plasmo Framework with-* example
title: "[EXP] "
labels: ["documentation"]
body:
  - type: markdown
    attributes:
      value: |
        **Thank you for taking the time to fill out this example request!** 🥳

  - type: textarea
    attributes:
      label: What is the example you wish to see?
      description: "Example: I would like to see more examples of how to use `X-Framework`."
    validations:
      required: true

  - type: textarea
    attributes:
      label: Is there any context that might help us understand?
      description: A clear description of any added context that might help us understand.
    validations:
      required: false

  - type: checkboxes
    id: terms
    attributes:
      label: Code of Conduct
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md).
      options:
        - label: I agree to follow this project's Code of Conduct
          required: true
        - label: I checked the [current issues](https://github.com/PlasmoHQ/plasmo/issues) for duplicate problems.
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Join our Discord server
    url: https://www.plasmo.com/s/d
    about: Ask questions and discuss with other community members


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thanks for opening a PR! Your contribution is much appreciated.
In order to make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below.
-->

## Details

This PR ...

### Code of Conduct

- [ ] I agree to follow this project's [Code of Conduct](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md)
- [ ] I agree to license this contribution under the MIT LICENSE
- [ ] I checked the [current PR](https://github.com/PlasmoHQ/plasmo/pulls) for duplication.

## Contacts

- (OPTIONAL) Discord ID:

If your PR is accepted, we will award you with the `Contributor` role on Discord server.

To join the server, visit: https://www.plasmo.com/s/d


================================================
FILE: .github/SECURITY.md
================================================
Contact: security@plasmo.com
Expires: 2100-01-01T00:00:00.000Z
Acknowledgments: https://www.plasmo.com/security/hall-of-fame


================================================
FILE: .github/workflows/test-examples.yml
================================================
name: Test examples
on:
  push:
    branches: ["main"]
  pull_request:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [20.x]
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - uses: actions/checkout@master
        with:
          submodules: "recursive"
      - name: Setup pnpm
        uses: pnpm/action-setup@v4.1.0
        with:
          run_install: false

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - uses: actions/cache@v4.2.3
        name: Setup pnpm cache
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: "Build local version of Plasmo"
        run: |
          pnpm i --no-frozen-lockfile
          pnpm run build:packages
          pnpm run build:api
          pnpm i
          pnpm run build:cli
          pnpm i

      - name: "Build all examples"
        run: pnpm run build:examples

      - name: "Test all examples"
        run: pnpm run test:examples
          


================================================
FILE: .github/workflows/test-package.yml
================================================
name: Test package manager execution
on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      tag:
        description: "Release tag to test, i.e latest, 1.0.0. Default latest"
        required: false
        default: latest

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [20.x]
        package-manager:
          [{ name: "pnpm", exec: "pnpm dlx" }, { name: "npm", exec: "npx -y" }]
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - name: "Install package manager"
        run: npm install -g ${{ matrix.package-manager.name }}
      - name: Check if Plasmo command works
        run: ${{ matrix.package-manager.exec }} plasmo@${{ github.event.inputs.tag }} version
      - name: Check if plasmo init works at all
        run: yes "lab" | ${{ matrix.package-manager.exec }} plasmo@${{ github.event.inputs.tag }} init --verbose
      - name: Check if building is possible and if it built
        run: |
          pushd lab
          ${{ matrix.package-manager.name }} run build
          pushd build
          popd
          timeout 10 ${{ matrix.package-manager.name }} run dev || code=$?; if [[ $code -ne 124 && $code -ne 0 ]]; then exit $code; fi


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
node_modules
.pnp
.pnp.js

# testing
coverage

# next.js
.next/
out/
build
dist/

# electron bundle
**/resources/app

# misc
.DS_Store
*.pem
.idea

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# turbo
.turbo

.tsbuildinfo


================================================
FILE: .gitmodules
================================================
[submodule "packages/rps"]
	path = packages/rps
	url = https://github.com/PlasmoHQ/rps.git
	branch = main
[submodule "packages/puro"]
	path = packages/puro
	url = https://github.com/PlasmoHQ/puro.git
	branch = main
[submodule "packages/gcp-refresh-token"]
	path = packages/gcp-refresh-token
	url = https://github.com/PlasmoHQ/gcp-refresh-token.git
	branch = main
[submodule "packages/use-hashed-state"]
	path = packages/use-hashed-state
	url = https://github.com/PlasmoHQ/use-hashed-state.git
	branch = main
[submodule "examples"]
	path = examples
	url = https://github.com/PlasmoHQ/examples.git
[submodule "packages/permission-ui"]
	path = packages/permission-ui
	url = https://github.com/PlasmoHQ/permission-ui.git
[submodule "packages/utils"]
	path = packages/utils
	url = https://github.com/PlasmoHQ/plasmo-utils.git
[submodule "packages/constants"]
	path = packages/constants
	url = https://github.com/PlasmoHQ/plasmo-constants.git
[submodule "packages/config"]
	path = packages/config
	url = https://github.com/PlasmoHQ/plasmo-config.git
[submodule "api/storage"]
	path = api/storage
	url = https://github.com/PlasmoHQ/storage.git
	branch = main


================================================
FILE: .npmrc
================================================
save-workspace-protocol = true
prefer-workspace-packages = true
save-exact = true
link-workspace-packages = true
strict-peer-dependencies = false
git-tag-version = false
commit-hooks = false

================================================
FILE: .prettierrc.mjs
================================================
/**
 * @type {import('prettier').Options}
 */
export default {
  printWidth: 80,
  tabWidth: 2,
  useTabs: false,
  semi: false,
  singleQuote: false,
  trailingComma: "none",
  bracketSpacing: true,
  bracketSameLine: true,
  plugins: ["@ianvs/prettier-plugin-sort-imports"],
  importOrder: [
    "<BUILTIN_MODULES>", // Node.js built-in modules
    "<THIRD_PARTY_MODULES>", // Imports not matched by other special words or groups.
    "", // Empty line
    "^@plasmo/(.*)$",
    "",
    "^@plasmohq/(.*)$",
    "",
    "^@plasmo-static-common/(.*)$",
    "",
    "^~(.*)$",
    "",
    "^[./]",
    "",
    "__plasmo_import_module__",
    "__plasmo_mount_content_script__"
  ]
}


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": ["esbenp.prettier-vscode"]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.formatOnSave": true,
  "files.exclude": {
    "**/.git": true,
    "**/.svn": true,
    "**/.hg": true,
    "**/CVS": true,
    "**/.DS_Store": true,
    "**/Thumbs.db": true,
    "**/node_modules": true,
    "**/.turbo": true,
    "**/.next": true,
    "**/*.log": true
  },
  "[svg]": {
    "editor.defaultFormatter": "jock.svg"
  },
  "git.detectSubmodulesLimit": 99
}


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: api/messaging/.gitignore
================================================
node_modules

# Lockfiles - See https://github.com/PlasmoHQ/p1asm0
pnpm-lock.yaml
package-lock.json
yarn.lock

.turbo

key.json
dist/


================================================
FILE: api/messaging/LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: api/messaging/jest.config.mjs
================================================
/**
 * @type {import('@jest/types').Config.InitialOptions}
 */

const config = {
  clearMocks: true,
  testEnvironment: "jsdom",
  extensionsToTreatAsEsm: [".ts"],
  transform: {
    "^.+.ts?$": [
      "ts-jest",
      {
        useESM: true,
        isolatedModules: true
      }
    ]
  },
  testMatch: ["**/*.test.ts"],
  verbose: true
}
export default config


================================================
FILE: api/messaging/package.json
================================================
{
  "name": "@plasmohq/messaging",
  "version": "0.7.2",
  "description": "Type-safe, zero-config messaging library for modern browser extensions",
  "type": "module",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    "./hook": {
      "types": "./dist/hook.d.ts",
      "import": "./dist/hook.js",
      "require": "./dist/hook.cjs"
    },
    "./relay": {
      "types": "./dist/relay.d.ts",
      "import": "./dist/relay.js",
      "require": "./dist/relay.cjs"
    },
    "./port": {
      "types": "./dist/port.d.ts",
      "import": "./dist/port.js",
      "require": "./dist/port.cjs"
    },
    "./pub-sub": {
      "types": "./dist/pub-sub.d.ts",
      "import": "./dist/pub-sub.js",
      "require": "./dist/pub-sub.cjs"
    },
    "./background": {
      "types": "./dist/background.d.ts",
      "import": "./dist/background.js",
      "require": "./dist/background.cjs"
    },
    "./message": {
      "types": "./dist/message.d.ts",
      "import": "./dist/message.js",
      "require": "./dist/message.cjs"
    },
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "typesVersions": {
    "*": {
      "message": [
        "./dist/message.d.ts"
      ],
      "hook": [
        "./dist/hook.d.ts"
      ],
      "relay": [
        "./dist/relay.d.ts"
      ],
      "port": [
        "./dist/port.d.ts"
      ],
      "pub-sub": [
        "./dist/pub-sub.d.ts"
      ],
      "background": [
        "./dist/background.d.ts"
      ]
    }
  },
  "files": [
    "dist"
  ],
  "tsup": {
    "entry": [
      "src/index.ts",
      "src/port.ts",
      "src/pub-sub.ts",
      "src/relay.ts",
      "src/message.ts",
      "src/background.ts",
      "src/hook.ts"
    ],
    "format": [
      "esm",
      "cjs"
    ],
    "target": "esnext",
    "platform": "node",
    "splitting": false,
    "bundle": true
  },
  "scripts": {
    "dev": "run-p dev:*",
    "dev:compile": "tsup --watch --sourcemap --dts-resolve",
    "dev:test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch",
    "build": "tsup --dts-resolve --minify --clean",
    "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
    "prepublishOnly": "pnpm build"
  },
  "author": "Plasmo Corp. <foss@plasmo.com>",
  "contributors": [
    "@louisgv",
    "@ColdSauce"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/PlasmoHQ/plasmo.git",
    "directory": "api/messaging"
  },
  "license": "MIT",
  "keywords": [
    "react-hook",
    "browser-extension",
    "chrome-extension"
  ],
  "peerDependencies": {
    "react": "^16.8.6 || ^17 || ^18 || ^19.0.0"
  },
  "peerDependenciesMeta": {
    "react": {
      "optional": true
    }
  },
  "devDependencies": {
    "@jest/globals": "29.7.0",
    "@jest/types": "29.6.3",
    "@testing-library/react": "16.2.0",
    "@testing-library/dom": "10.4.0",
    "@types/chrome": "0.0.312",
    "@types/node": "22.13.13",
    "@types/react": "19.0.12",
    "canvas": "3.1.0",
    "cross-env": "7.0.3",
    "jest": "29.7.0",
    "jest-environment-jsdom": "29.7.0",
    "react": "19.0.0",
    "react-dom": "19.0.0",
    "rimraf": "6.0.1",
    "ts-jest": "29.3.0",
    "tsup": "8.4.0",
    "typescript": "5.8.2"
  },
  "dependencies": {
    "nanoid": "5.1.5"
  }
}


================================================
FILE: api/messaging/src/background.ts
================================================
import type { PlasmoMessaging, PortName } from "./index"
import { getExtRuntime } from "./utils"

export const getPortMap = (): Map<PortName, chrome.runtime.Port> =>
  globalThis.__plasmoInternalPortMap

export const getPort = (name: PortName): chrome.runtime.Port => {
  const portMap = getPortMap()
  const port = portMap.get(name)
  if (!port) {
    throw new Error(`Port ${name} not found`)
  }
  return port
}

getExtRuntime().onMessage.addListener(
  (request: PlasmoMessaging.InternalRequest, _sender, sendResponse) => {
    switch (request.__PLASMO_INTERNAL_SIGNAL__) {
      case "__PLASMO_MESSAGING_PING__": {
        sendResponse(true)
        break
      }
    }

    return true
  }
)


================================================
FILE: api/messaging/src/hook.ts
================================================
import { useEffect, useRef, useState } from "react"

import { relayMessage, type MessageName, type PlasmoMessaging } from "./index"
import { listen as messageListen } from "./message"
import { listen as portListen } from "./port"
import { relay } from "./relay"

/**
 * Used in any extension context to listen and send messages to background.
 */
export const useMessage = <RequestBody, ResponseBody>(
  handler: PlasmoMessaging.Handler<string, RequestBody, ResponseBody>
) => {
  const [data, setData] = useState<RequestBody>()

  useEffect(
    () =>
      messageListen<RequestBody, ResponseBody>(async (req, res) => {
        setData(req.body)
        await handler(req, res)
      }),
    [handler]
  )

  return {
    data
  }
}

export const usePort: PlasmoMessaging.PortHook = (name) => {
  const portRef = useRef<chrome.runtime.Port>(undefined)
  const reconnectRef = useRef(0)
  const [data, setData] = useState()

  useEffect(() => {
    if (!name) {
      return null
    }

    const { port, disconnect } = portListen(
      name,
      (msg) => {
        setData(msg)
      },
      () => {
        reconnectRef.current = reconnectRef.current + 1
      }
    )

    portRef.current = port
    return disconnect
  }, [
    name,
    reconnectRef.current // This is needed to force a new port ref
  ])

  return {
    data,
    send: (body) => {
      portRef.current.postMessage({
        name,
        body
      })
    },
    listen: (handler) => portListen(name, handler)
  }
}

/**
 * TODO: Perhaps add a way to detect if this hook is being used inside CS?
 */
export function useMessageRelay<RequestBody = any>(
  req: PlasmoMessaging.Request<MessageName, RequestBody>
) {
  useEffect(() => relayMessage(req), [])
}

export const useRelay: PlasmoMessaging.RelayFx = (req, onMessage) => {
  const relayRef = useRef<() => void>(undefined)

  useEffect(() => {
    relayRef.current = relay(req, onMessage)
    return relayRef.current
  }, [])

  return () => relayRef.current?.()
}


================================================
FILE: api/messaging/src/index.ts
================================================
import { relay as rawRelay, sendViaRelay as rawSendViaRelay } from "./relay"
import type { MessageName, PlasmoMessaging } from "./types"
import { getActiveTab, getExtRuntime, getExtTabs } from "./utils"

export type {
  PlasmoMessaging,
  MessageName,
  PortName,
  PortsMetadata,
  MessagesMetadata,
  OriginContext
} from "./types"

/**
 * Send to Background Service Workers from Content Scripts or Extension pages.
 * `extensionId` is required to send a message from a Content Script in the main world
 */
// TODO: Add a framework runtime check, using a global variable
export const sendToBackground: PlasmoMessaging.SendFx<MessageName> = async (
  req
) => {
  return getExtRuntime().sendMessage(req.extensionId ?? null, req)
}

/**
 * Send to Content Scripts from Extension pages or Background Service Workers.
 * Default to active tab if no tabId is provided in the request
 */
export const sendToContentScript: PlasmoMessaging.SendFx = async (req) => {
  const tabId =
    typeof req.tabId === "number" ? req.tabId : (await getActiveTab())?.id

  if (!tabId) {
    throw new Error("No active tab found to send message to.")
  }

  return getExtTabs().sendMessage(tabId, req)
}

/**
 * @deprecated Renamed to `sendToContentScript`
 */

export const sendToActiveContentScript = sendToContentScript

/**
 * Any request sent to this relay get send to background, then emitted back as a response
 */
export const relayMessage: PlasmoMessaging.MessageRelayFx = (req) =>
  rawRelay(req, sendToBackground)

/**
 * @deprecated Migrated to `relayMessage`
 */
export const relay = relayMessage

export const sendToBackgroundViaRelay: PlasmoMessaging.SendFx<MessageName> =
  rawSendViaRelay

/**
 * @deprecated Migrated to `sendToBackgroundViaRelay`
 */
export const sendViaRelay = sendToBackgroundViaRelay


================================================
FILE: api/messaging/src/message.ts
================================================
import { type PlasmoMessaging } from "./index"
import { getExtRuntime } from "./utils"

export const listen = <RequestBody, ResponseBody>(
  handler: PlasmoMessaging.Handler<string, RequestBody, ResponseBody>
) => {
  const metaListener = async (
    req: any,
    sender: chrome.runtime.MessageSender,
    sendResponse: (response?: ResponseBody) => void
  ) => {
    await handler?.(
      {
        ...req,
        sender
      },
      {
        send: (p) => sendResponse(p)
      }
    )
  }

  const listener = (
    req: any,
    sender: chrome.runtime.MessageSender,
    sendResponse: (response?: ResponseBody) => void
  ) => {
    metaListener(req, sender, sendResponse)
    return true // Synchronous return to indicate this is an async listener
  }

  getExtRuntime().onMessage.addListener(listener)
  return () => {
    getExtRuntime().onMessage.removeListener(listener)
  }
}


================================================
FILE: api/messaging/src/port.ts
================================================
import type { PortName } from "./index"
import { getExtRuntime } from "./utils"

const portMap = new Map<PortName, chrome.runtime.Port>()

export const getPort = (name: PortName) => {
  const port = portMap.get(name)
  if (!!port) {
    return port
  }
  const newPort = getExtRuntime().connect({ name })
  portMap.set(name, newPort)
  return newPort
}

export const removePort = (name: PortName) => {
  portMap.delete(name)
}

export const listen = <ResponseBody = any>(
  name: PortName,
  handler: (msg: ResponseBody) => Promise<void> | void,
  onReconnect?: () => void
) => {
  const port = getPort(name)

  function reconnectHandler() {
    removePort(name)
    onReconnect?.()
  }

  port.onMessage.addListener(handler)
  port.onDisconnect.addListener(reconnectHandler)

  return {
    port,
    disconnect: () => {
      port.onMessage.removeListener(handler)
      port.onDisconnect.removeListener(reconnectHandler)
    }
  }
}


================================================
FILE: api/messaging/src/pub-sub.ts
================================================
import { getExtRuntime } from "./utils"

export type PubSubMessage = {
  from?: number
  to?: number
  payload: any
}

// Only usable from BGSW
export const getHubMap = (): Map<number, chrome.runtime.Port> =>
  globalThis.__plasmoInternalHubMap

// Only usable by BGSW
export const startHub = () => {
  const runtime = getExtRuntime()
  if (!runtime.onConnectExternal) {
    throw new Error(
      "onConnect External not available. You need externally_connectable entry possibly"
    )
  }

  globalThis.__plasmoInternalHubMap = new Map()
  const hub = getHubMap()

  runtime.onConnectExternal.addListener((port) => {
    const tabId = port.sender.tab.id
    if (!hub.has(tabId)) {
      hub.set(tabId, port)
      port.onMessage.addListener((message) => {
        broadcast({ from: tabId, payload: message })
      })
      port.onDisconnect.addListener(() => {
        //TODO - Should we log?
        hub.delete(tabId)
      })
    }
  })
}

// Only usable by BGSW
export const broadcast = (pubSubMessage: PubSubMessage) => {
  const hub = getHubMap()
  hub.forEach((port, tabId) => {
    const skipBroadcast = tabId === pubSubMessage.from
    if (skipBroadcast) {
      return
    }
    port.postMessage({ ...pubSubMessage, to: tabId })
  })
}

export const connectToHub = (extensionId: string) => {
  const runtime = getExtRuntime()
  if (!runtime.connect) {
    throw new Error(
      "runtime.connect not available. You need to use startHub in BGSW"
    )
  }
  const port = runtime.connect(extensionId)
  return port
}


================================================
FILE: api/messaging/src/relay.test.ts
================================================
import { beforeEach, describe, expect, jest, test } from "@jest/globals"

import type { PlasmoMessaging } from "./types"

const { relay, sendViaRelay } = await import("./relay")

class MessagePortMock {
  callbacks = new Set<Function>()
  addEventListener = (_message, callback) => {
    this.callbacks.add(callback)
  }
  removeEventListener = (_message, callback) => {
    this.callbacks.delete(callback)
  }
  postMessage = (data) => {
    const event = {
      data,
      source: globalThis.window
    }

    this.callbacks.forEach((callback) => {
      callback(event)
    })
  }
  clear() {
    this.callbacks.clear()
  }
}

/**
 * Message port callbacks happen synchronously
 * But promises get resolved in event queue
 */
const waitForMicroTasks = () => Promise.resolve()

describe("sendViaRelay", () => {
  const port = new MessagePortMock()
  const req: PlasmoMessaging.Request = {
    name: "test",
    body: { foo: "bar" },
    relayId: "1"
  }

  beforeEach(() => {
    port.clear()
  })

  test("posts message to provided message port", (done) => {
    port.addEventListener("message", (event) => {
      expect(event.data).toMatchObject(req)
      done()
    })

    sendViaRelay(req, port)
    expect(port.callbacks.size).toBe(2)
  })

  test("appends random instanceId to relayed request", (done) => {
    port.addEventListener("message", (event) => {
      expect(Object.hasOwn(event.data, "instanceId")).toBeTruthy()
      done()
    })

    sendViaRelay(req, port)
  })

  test("only resolves body with matching instanceId", (done) => {
    let response = null

    port.addEventListener("message", async (event) => {
      if (event.data.relayed) {
        return
      }

      port.postMessage({
        ...event.data,
        body: { bar: "foo" },
        relayed: true,
        instanceId: "123"
      })
      await waitForMicroTasks()

      expect(response).toEqual(null)

      port.postMessage({
        ...event.data,
        body: { bar: "foo" },
        relayed: true
      })
      await waitForMicroTasks()

      expect(response).toEqual({ bar: "foo" })

      done()
    })

    sendViaRelay(req, port).then((res) => (response = res))
  })
})

describe("relay", () => {
  const port = new MessagePortMock()
  const req: PlasmoMessaging.Request = {
    name: "test",
    relayId: "1"
  }
  const handler = (data) => {
    return Promise.resolve({ echo: data.body })
  }

  beforeEach(() => {
    port.clear()
  })

  test("returns cleanup function", () => {
    const cleanup = relay(req, handler, port)

    expect(port.callbacks.size).toBe(1)

    cleanup()

    expect(port.callbacks.size).toBe(0)
  })

  test("does not handle relayed messages", () => {
    const notCalledHandler = jest.fn((data) => Promise.resolve(data))
    relay(req, notCalledHandler, port)

    port.postMessage({ ...req, relayed: true })

    expect(notCalledHandler).not.toBeCalled()
  })

  test("posts back resolution result and instanceId", (done) => {
    relay(req, handler, port)

    const mockedReq = { ...req, instanceId: "123", body: { foo: "bar" } }

    port.addEventListener("message", async (event) => {
      if (!event.data.relayed) {
        return
      }

      expect(event.data.body).toEqual({ echo: mockedReq.body })
      expect(event.data.instanceId).toEqual(mockedReq.instanceId)

      done()
    })

    port.postMessage(mockedReq)
  })
})


================================================
FILE: api/messaging/src/relay.ts
================================================
import { nanoid } from "nanoid"

import type { PlasmoMessaging } from "./index"
import { isSameOrigin } from "./utils"

/**
 * Raw relay abstracting window.postMessage
 */
export const relay: PlasmoMessaging.RelayFx = (
  req,
  onMessage,
  messagePort = globalThis.window
) => {
  const relayHandler = async (
    event: MessageEvent<PlasmoMessaging.RelayMessage>
  ) => {
    if (isSameOrigin(event, req) && !event.data.relayed) {
      const relayPayload = {
        name: req.name,
        relayId: req.relayId,
        body: event.data.body
      }

      const backgroundResponse = await onMessage?.(relayPayload)

      messagePort.postMessage(
        {
          name: req.name,
          relayId: req.relayId,
          instanceId: event.data.instanceId,
          body: backgroundResponse,
          relayed: true
        },
        {
          targetOrigin: req.targetOrigin || "/"
        }
      )
    }
  }

  messagePort.addEventListener("message", relayHandler)
  return () => messagePort.removeEventListener("message", relayHandler)
}

export const sendViaRelay: PlasmoMessaging.SendFx = (
  req,
  messagePort = globalThis.window
) =>
  new Promise((resolve, _reject) => {
    const instanceId = nanoid()
    const abortController = new AbortController()
    messagePort.addEventListener(
      "message",
      (event: MessageEvent<PlasmoMessaging.RelayMessage>) => {
        if (
          isSameOrigin(event, req) &&
          event.data.relayed &&
          event.data.instanceId === instanceId
        ) {
          resolve(event.data.body)
          abortController.abort()
        }
      },
      {
        signal: abortController.signal
      }
    )

    messagePort.postMessage(
      {
        ...req,
        instanceId
      } as PlasmoMessaging.RelayMessage,
      {
        targetOrigin: req.targetOrigin || "/"
      }
    )
  })


================================================
FILE: api/messaging/src/types.ts
================================================
export interface MessagesMetadata {}
export interface PortsMetadata {}

export type MessageName = keyof MessagesMetadata
export type PortName = keyof PortsMetadata

export type InternalSignal = "__PLASMO_MESSAGING_PING__"

export namespace PlasmoMessaging {
  export type Request<TName = any, TBody = any> = {
    name: TName

    extensionId?: string
    port?: chrome.runtime.Port
    sender?: chrome.runtime.MessageSender

    body?: TBody
    tabId?: number
    relayId?: string

    // Target origin to send the message to (for relay), default to "/"
    targetOrigin?: string
  }

  export type RelayMessage<TName = any, TBody = any> = Request<TName, TBody> & {
    /**
     * Used to resolve corresponding window.postMessage messages
     */
    instanceId: string
    relayed: boolean
  }

  export type InternalRequest = {
    __PLASMO_INTERNAL_SIGNAL__: InternalSignal
  }

  export type Response<TBody = any> = {
    send: (body: TBody) => void
  }

  export type InternalHandler = (request: InternalRequest) => void

  export type Handler<
    RequestName = string,
    RequestBody = any,
    ResponseBody = any
  > = (
    request: Request<RequestName, RequestBody>,
    response: Response<ResponseBody>
  ) => void | Promise<void> | boolean

  export type PortHandler<RequestBody = any, ResponseBody = any> = Handler<
    PortName,
    RequestBody,
    ResponseBody
  >

  export type MessageHandler<RequestBody = any, ResponseBody = any> = Handler<
    MessageName,
    RequestBody,
    ResponseBody
  >

  export interface SendFx<TName = string> {
    <RequestBody = any, ResponseBody = any>(
      request: Request<TName, RequestBody>,
      messagePort?:
        | Pick<
            MessagePort,
            "addEventListener" | "removeEventListener" | "postMessage"
          >
        | Window
    ): Promise<ResponseBody>
  }

  export interface RelayFx {
    <RelayName = any, RequestBody = any, ResponseBody = any>(
      request: Request<RelayName, RequestBody>,
      onMessage?: (
        request: Request<RelayName, RequestBody>
      ) => Promise<ResponseBody>,
      messagePort?:
        | Pick<
            MessagePort,
            "addEventListener" | "removeEventListener" | "postMessage"
          >
        | Window
    ): () => void
  }

  export interface MessageRelayFx {
    <RequestBody = any>(request: Request<MessageName, RequestBody>): () => void
  }

  export interface PortHook {
    <TRequestBody = Record<string, any>, TResponseBody = any>(
      name: PortName
    ): {
      data?: TResponseBody
      send: (payload: TRequestBody) => void
      listen: <T = TResponseBody>(
        handler: (msg: T) => void
      ) => {
        port: chrome.runtime.Port
        disconnect: () => void
      }
    }
  }
}

export type OriginContext =
  | "background"
  | "extension-page"
  | "sandbox-page"
  | "content-script"
  | "window"


================================================
FILE: api/messaging/src/utils.ts
================================================
import type { PlasmoMessaging } from "./index"

const extTabs = (globalThis.browser?.tabs ||
  globalThis.chrome?.tabs) as typeof chrome.tabs

export const getExtRuntime = () => {
  // TODO: Move this to a broader utils package later on
  const extRuntime = (globalThis.browser?.runtime ||
    globalThis.chrome?.runtime) as typeof chrome.runtime
  
  if (!extRuntime) {
    throw new Error("Extension runtime is not available")
  }
  return extRuntime
}

export const getExtTabs = () => {
  if (!extTabs) {
    throw new Error("Extension tabs API is not available")
  }
  return extTabs
}

export const getActiveTab = async () => {
  const extTabs = getExtTabs()
  const [tab] = await extTabs.query({
    active: true,
    currentWindow: true
  })
  return tab as chrome.tabs.Tab | undefined
}

export const isSameOrigin = (
  event: MessageEvent,
  req: any
): req is PlasmoMessaging.Request =>
  !req.__internal &&
  event.source === globalThis.window &&
  event.data.name === req.name &&
  (req.relayId === undefined || event.data.relayId === req.relayId)

export const getRuntimeContext = () => {
  // If chrome API available but they cannot access
  // OR we can mark them directly (?), by injecting a tag at runtime itself
}


================================================
FILE: api/messaging/tsconfig.json
================================================
{
  "compilerOptions": {
    "outDir": "dist",
    "resolveJsonModule": true,
    "sourceMap": true,
    "strict": false,
    "declaration": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "esModuleInterop": true,
    "noImplicitAny": false,
    "moduleResolution": "node",

    "module": "ESNext",
    "target": "ESNext",
    "allowJs": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src/**/*.ts"]
}


================================================
FILE: api/persistent/.gitignore
================================================
node_modules

# Lockfiles - See https://github.com/PlasmoHQ/p1asm0
pnpm-lock.yaml
package-lock.json
yarn.lock

.turbo

key.json
dist/


================================================
FILE: api/persistent/LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: api/persistent/README.md
================================================
# Plasmo Persistent runtime

This library contains a couple of hacks to keep the BGSW alive for MV3 transitioning.

Usage in a background service worker:

```ts
import { keepAlive } from "@plasmohq/persistent/background"

keepAlive()
```


================================================
FILE: api/persistent/package.json
================================================
{
  "name": "@plasmohq/persistent",
  "version": "0.0.6",
  "description": "A couple of hacks to keep the BGSW alive in a library",
  "type": "module",
  "module": "./src/index.ts",
  "types": "./src/index.ts",
  "typesVersions": {
    "*": {
      "background": [
        "./src/background.ts"
      ]
    }
  },
  "publishConfig": {
    "module": "./dist/index.js",
    "types": "./dist/index.d.ts",
    "typesVersions": {
      "*": {
        "background": [
          "./dist/background.d.ts"
        ]
      }
    }
  },
  "exports": {
    "./background": {
      "types": "./dist/background.d.ts",
      "import": "./dist/background.js",
      "require": "./dist/background.cjs"
    },
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "dev": "run-p dev:*",
    "dev:compile": "tsup --watch",
    "dev:test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch",
    "build": "tsup",
    "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
    "prepublishOnly": "pnpm build"
  },
  "author": "Plasmo Corp. <foss@plasmo.com>",
  "contributors": [
    "@louisgv"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/PlasmoHQ/plasmo.git",
    "directory": "api/persistent"
  },
  "license": "MIT",
  "keywords": [
    "browser-extension",
    "chrome-extension"
  ],
  "devDependencies": {
    "@jest/globals": "29.7.0",
    "@jest/types": "29.6.3",
    "@plasmo/config": "workspace:*",
    "@types/chrome": "0.0.312",
    "@types/node": "22.13.13",
    "cross-env": "7.0.3",
    "jest": "29.7.0",
    "jest-environment-jsdom": "29.7.0",
    "ts-jest": "29.3.0",
    "tsup": "8.4.0",
    "typescript": "5.8.2"
  }
}


================================================
FILE: api/persistent/src/background.ts
================================================
export const keepAlive = () => {
  const extRuntime = (globalThis.browser?.runtime ||
    globalThis.chrome?.runtime) as typeof chrome.runtime
  const _keepAlive = () => setInterval(extRuntime.getPlatformInfo, 24_000) // 24 seconds
  extRuntime.onStartup.addListener(_keepAlive)
  _keepAlive()
}


================================================
FILE: api/persistent/src/index.ts
================================================
export const life = 42


================================================
FILE: api/persistent/tsconfig.json
================================================
{
  "extends": "@plasmo/config/ts/utils",
  "include": ["src/**/*.ts"]
}


================================================
FILE: api/persistent/tsup.config.ts
================================================
import { defineConfig } from "tsup"

export default defineConfig((opt) => {
  const isProd = !opt.watch
  return {
    entry: ["src/index.ts", "src/background.ts"],

    format: ["esm", "cjs"],

    target: "esnext",
    platform: "node",
    splitting: false,
    bundle: true,
    dts: true,

    watch: opt.watch,
    sourcemap: !isProd,
    minify: isProd,
    clean: isProd
  }
})


================================================
FILE: api/selector/.gitignore
================================================
node_modules

# Lockfiles - See https://github.com/PlasmoHQ/p1asm0
pnpm-lock.yaml
package-lock.json
yarn.lock

.turbo

key.json
dist/


================================================
FILE: api/selector/LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: api/selector/package.json
================================================
{
  "name": "@plasmohq/selector",
  "version": "0.0.7",
  "description": "Powerful Selector API with Dedicated Monitoring Supports",
  "type": "module",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    "./hook": {
      "types": "./dist/hook.d.ts",
      "import": "./dist/hook.js",
      "require": "./dist/hook.cjs"
    },
    "./monitor": {
      "types": "./dist/monitor.d.ts",
      "import": "./dist/monitor.js",
      "require": "./dist/monitor.cjs"
    },
    "./background": {
      "types": "./dist/background.d.ts",
      "import": "./dist/background.js",
      "require": "./dist/background.cjs"
    },
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "typesVersions": {
    "*": {
      "hook": [
        "./dist/hook.d.ts"
      ],
      "monitor": [
        "./dist/monitor.d.ts"
      ],
      "background": [
        "./dist/background.d.ts"
      ]
    }
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "dev": "run-p dev:*",
    "dev:compile": "tsup --watch",
    "dev:test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch",
    "build": "tsup",
    "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
    "prepublishOnly": "pnpm build"
  },
  "author": "Plasmo Corp. <foss@plasmo.com>",
  "contributors": [
    "@louisgv",
    "@ColdSauce"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/PlasmoHQ/plasmo.git",
    "directory": "api/selector"
  },
  "license": "MIT",
  "keywords": [
    "react-hook",
    "browser-extension",
    "chrome-extension"
  ],
  "peerDependencies": {
    "react": "^16.8.6 || ^17 || ^18 || ^19.0.0"
  },
  "peerDependenciesMeta": {
    "react": {
      "optional": true
    }
  },
  "devDependencies": {
    "@jest/globals": "29.7.0",
    "@jest/types": "29.6.3",
    "@testing-library/react": "16.2.0",
    "@types/chrome": "0.0.312",
    "@types/node": "22.13.13",
    "@types/react": "19.0.12",
    "cross-env": "7.0.3",
    "jest": "29.7.0",
    "jest-environment-jsdom": "29.7.0",
    "react": "19.0.0",
    "react-dom": "19.0.0",
    "rimraf": "6.0.1",
    "ts-jest": "29.3.0",
    "tsup": "8.4.0",
    "typescript": "5.8.2"
  }
}


================================================
FILE: api/selector/src/background.ts
================================================
import type { SelectorMessage } from "./types"

// Simple cache, it won't persist, but it will do for now
const softCache = new Set()

async function selectorMessageHandler(
  message: SelectorMessage,
  monitorId: string,
  sample: number
) {
  switch (message.name) {
    case "plasmo:selector:invalid": {
      if (!monitorId) {
        return
      }

      const body = JSON.stringify({
        monitorId,
        payload: message.payload
      })

      if (softCache.has(body) || Math.random() > sample) {
        return
      }

      try {
        softCache.add(body)
        await fetch(
          `${process.env.ITERO_MONITOR_API_BASE_URI}/api/selector/invalid`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json"
            },
            body
          }
        )
      } catch {}
    }
  }
}

/**
 * @param monitorId id of the monitor to send invalid selectors to
 * @param sample percentage of invalid selectors to send to the monitor, default 47%
 */
export const init = ({ monitorId = "", sample = 0.47 }) => {
  chrome.runtime.onMessage.addListener((message: SelectorMessage) => {
    selectorMessageHandler(message, monitorId, sample)
    return true
  })
}


================================================
FILE: api/selector/src/hook.ts
================================================
export const useSelector = () => {}


================================================
FILE: api/selector/src/index.ts
================================================
import type { SelectorMessage } from "./types"

async function sendInvalidSelectors(selectors: string[]) {
  try {
    return await chrome?.runtime?.sendMessage({
      name: "plasmo:selector:invalid",
      payload: {
        selectors,
        url: window.location.href
      }
    } as SelectorMessage)
  } catch {}
}

export const querySelectors = (selectors: string[]) => {
  const result: Element[] = []
  const invalidSelectors: string[] = []
  for (const selector of selectors) {
    const element = document.querySelector(selector)
    if (!element) {
      invalidSelectors.push(selector)
    } else {
      result.push(element)
    }
  }

  if (invalidSelectors.length > 0) {
    sendInvalidSelectors(invalidSelectors)
  }

  return result
}

export const querySelector = (selector: string) => {
  const element = document.querySelector(selector)
  if (!element) {
    sendInvalidSelectors([selector])
  }

  return element
}

export const querySelectorAll = (selector: string) => {
  const elements = document.querySelectorAll(selector)

  if (elements.length === 0) {
    sendInvalidSelectors([selector])
  }

  return elements
}


================================================
FILE: api/selector/src/monitor.ts
================================================
export {}

document.querySelector = new Proxy(document.querySelector, {
  apply: (target, thisArg, args) => {}
})


================================================
FILE: api/selector/src/types.ts
================================================
export type SelectorMessage = {
  name: "plasmo:selector:invalid"
  payload: {
    selectors: string[]
    url: string
  }
}


================================================
FILE: api/selector/tsconfig.json
================================================
{
  "compilerOptions": {
    "outDir": "dist",
    "resolveJsonModule": true,
    "sourceMap": true,
    "strict": false,
    "declaration": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "esModuleInterop": true,
    "noImplicitAny": false,
    "moduleResolution": "node",

    "module": "ESNext",
    "target": "ESNext",
    "allowJs": true,
    "verbatimModuleSyntax": true,
    "lib": ["DOM"]
  },
  "include": ["src/**/*.ts"]
}


================================================
FILE: api/selector/tsup.config.ts
================================================
import { defineConfig } from "tsup"

export default defineConfig((opt) => {
  const isProd = !opt.watch
  return {
    entry: [
      "src/index.ts",
      "src/monitor.ts",
      "src/background.ts",
      "src/hook.ts"
    ],

    format: ["esm", "cjs"],

    target: "esnext",
    platform: "node",
    splitting: false,
    bundle: true,
    dts: true,

    env: {
      ITERO_MONITOR_API_BASE_URI: isProd
        ? "https://itero.plasmo.com"
        : "http://localhost:3000"
    },

    watch: opt.watch,
    sourcemap: !isProd,
    minify: isProd,
    clean: isProd
  }
})


================================================
FILE: cli/create-plasmo/bin/index.mjs
================================================
#!/usr/bin/env node

import "../dist/index.js"


================================================
FILE: cli/create-plasmo/package.json
================================================
{
  "name": "create-plasmo",
  "version": "0.90.5",
  "description": "Create Plasmo Framework Browser Extension",
  "main": "dist/index.js",
  "bin": "bin/index.mjs",
  "type": "module",
  "files": [
    "bin",
    "dist"
  ],
  "tsup": {
    "format": "esm",
    "target": "esnext",
    "platform": "node",
    "splitting": false,
    "bundle": true,
    "minify": true,
    "clean": true,
    "banner": {
      "js": "import { createRequire } from 'module';const require = createRequire(import.meta.url);"
    }
  },
  "scripts": {
    "build": "tsup src/index.ts",
    "prepublishOnly": "run-s build"
  },
  "author": "Plasmo Corp. <support@plasmo.com>",
  "homepage": "https://docs.plasmo.com/",
  "repository": {
    "type": "git",
    "url": "https://github.com/PlasmoHQ/plasmo.git",
    "directory": "cli/create-plasmo"
  },
  "license": "MIT",
  "keywords": [
    "plasmo",
    "browser-extensions",
    "framework"
  ],
  "dependencies": {
    "@plasmohq/init": "workspace:*"
  },
  "devDependencies": {
    "@plasmo/config": "workspace:*",
    "@plasmo/constants": "workspace:*",
    "@plasmo/utils": "workspace:*",
    "plasmo": "workspace:*",
    "typescript": "5.8.2"
  }
}


================================================
FILE: cli/create-plasmo/src/commands.ts
================================================
export const validCommandList = []


================================================
FILE: cli/create-plasmo/src/index.ts
================================================
#!/usr/bin/env node
import { argv, exit } from "process"
import { version } from "plasmo/package.json"
import init from "plasmo/src/commands/init"

import { ErrorMessage } from "@plasmo/constants/error"
import { aLog, eLog } from "@plasmo/utils/logging"
import { exitCountDown } from "@plasmo/utils/wait"

process.env.APP_VERSION = version

async function main() {
  try {
    // In case someone pasted an essay into the cli
    if (argv.length > 10) {
      throw new Error(ErrorMessage.TooManyArg)
    }

    argv.splice(2, 0, "init")

    await init()
  } catch (e) {
    eLog((e as Error).message || ErrorMessage.Unknown)
    aLog(e.stack)
    await exitCountDown(3)
    exit(1)
  }
}

main()

process.on("SIGINT", () => exit(0))
process.on("SIGTERM", () => exit(0))


================================================
FILE: cli/create-plasmo/tsconfig.json
================================================
{
  "extends": "@plasmo/config/ts/cli.json",
  "include": ["src/**/*.ts"],
  "exclude": ["dist", "node_modules", "templates"],
  "compilerOptions": {
    "outDir": "dist",
    "baseUrl": ".",
    "paths": {
      "~features/*": ["../plasmo/src/features/*"],
      "~commands": ["./src/commands"]
    }
  }
}


================================================
FILE: cli/plasmo/.eslintrc.js
================================================
module.exports = require("@plasmo/config/eslint-preset")


================================================
FILE: cli/plasmo/LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: cli/plasmo/README.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="See License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Follow PlasmoHQ on Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Watch our Live DEMO every Friday" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Join our Discord for support and chat about our projects" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  English | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

**Production Cloud:** We've built a cloud offering for browser extensions called [Itero](https://itero.plasmo.com). Check it out if you want instant beta testing and more awesome features.

# Plasmo Framework

The [Plasmo](https://www.plasmo.com/) Framework is a battery-packed browser extension SDK made by hackers for hackers. Build your product and stop worrying about config files and the odd peculiarities of building browser extensions.

> It's like [Next.js](https://nextjs.org/) for browser extensions!

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## Highlighted Features

- First-class [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/) Support
- [Declarative Development](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- [Content Scripts UI](https://docs.plasmo.com/csui)
- [Tab Pages](https://docs.plasmo.com/framework/tab-pages)
- Live-reloading + React HMR
- [`.env*` files](https://docs.plasmo.com/framework/env)
- [Storage API](https://docs.plasmo.com/framework/storage)
- [Messaging API](https://docs.plasmo.com/framework/messaging)
- [Remote code bundling](https://docs.plasmo.com/framework/remote-code) (e.g., for Google Analytics)
- Targeting [multiple browser and manifest pairs](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- [Automated deployment via BPP](https://docs.plasmo.com/framework/workflows/submit)
- Optional support for [Svelte](https://github.com/PlasmoHQ/with-svelte) and [Vue](https://github.com/PlasmoHQ/with-vue)

And many, many more! 🚀

## System Requirements

- Node.js 16.x or later
- MacOS, Windows, or Linux
- (Strongly Recommended) [pnpm](https://pnpm.io/)

## Examples

We have examples showcasing how one can use Plasmo with [Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss), and many more. To check them out, [visit our examples repository](https://github.com/PlasmoHQ/examples).

## Documentation

Check out the [documentation](https://docs.plasmo.com/) to get a more in-depth view into the Plasmo Framework.

## Browser Extensions Book

For a more in-depth view into how browser extensions work, and how to develop them, we highly recommend Matt Frisbie's new book ["Building Browser Extensions"](https://buildingbrowserextensions.com/plasmo)

## Usage

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

The road ahead is filled with many turns.

- Popup changes go in `popup.tsx`
- Options page changes go in `options.tsx`
- Content script changes go in `content.ts`
- Background service worker changes go in `background.ts`

### Directories

You can also organize these files in their own directories:

```
ext-dir
├───assets
|   └───icon.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

Finally, you can also avoid putting source code in your root directory by putting them in a `src` sub-directory, [following this guide](https://docs.plasmo.com/framework/customization/src). Note that `assets` and other config files will still need to be in the root directory.

## Supported Browsers

To see a list of supported browser targets, [please refer to our documentation here](https://docs.plasmo.com/framework/workflows/faq#what-are-the-officially-supported-browser-targets).

## Community

The Plasmo community can be found on [Discord](https://www.plasmo.com/s/d). This is the appropriate channel to get help with using the Plasmo Framework.

Our [Code of Conduct](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) applies to all Plasmo community channels.

## Contributing

Please see the [contributing guidelines](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) to learn more.

A big thanks to all of our amazing [contributors](https://github.com/PlasmoHQ/plasmo/graphs/contributors) ❤️

Feel free to join the fun and send a PR!

### Plasmo Framework

<a href="https://github.com/PlasmoHQ/plasmo/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/plasmo" />
</a>

### [Plasmo Examples](https://github.com/PlasmoHQ/examples)

<a href="https://github.com/PlasmoHQ/examples/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/examples" />
</a>

### [Plasmo Storage](https://github.com/PlasmoHQ/storage)

<a href="https://github.com/PlasmoHQ/storage/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/storage" />
</a>

### [Browser Platform Publisher](https://github.com/PlasmoHQ/bpp)

<a href="https://github.com/PlasmoHQ/bpp/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/bpp" />
</a>

## Disclaimer

Plasmo is currently alpha software, and some things might change from version to version, so please be mindful and use it at your own risk.

# License

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/bin/index.mjs
================================================
#!/usr/bin/env node

import "../dist/index.js"


================================================
FILE: cli/plasmo/i18n/README.de-DE.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="License anzeigen" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Folge PlasmoHQ auf Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Schaue unsere Live DEMO jeden Freitag an" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Trete unserem Discord server bei, um zu chatten und Unterstützung für Projekte zu bekommen" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | Deutsch | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

# Plasmo Framework

Das [Plasmo](https://www.plasmo.com/) Framework ist ein SDK zum Erstellen von Browser-Erweiterungen, das von Hackern für Hacker entwickelt wurde. Erstelle dein Produkt, ohne dir Gedanken über Konfigurationsdateien und die seltsamen Eigenheiten der Erstellung von Browsererweiterungen machen zu müssen.

> Es ist wie [Next.js](https://nextjs.org/) für Browser-Erweiterungen!

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## Features

- Direkte Unterstützung von [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/)
- [Deklarative Entwicklung mit automatischer Erzeugung von "manifest.json" (MV3)](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- Automatisches Neuladen
- [`.env*` Datei-Unterstützung](https://docs.plasmo.com/framework/env)
- [Bundling von externen Skripten](https://docs.plasmo.com/framework/workflows/remote-code) (z.B. für gtag4)
- Automatisierte Bereitstellung (über [BPP](https://docs.plasmo.com/framework/workflows/submit))
- Und viel, viel mehr! 🚀

## Systemanforderungen

- Node.js 16.x oder neuer
- MacOS, Windows oder Linux
- (Stark empfohlen) [pnpm](https://pnpm.io/)

## Beispiele

Wir haben Beispiele, die zeigen, wie man Plasmo mit [Firebase-Authentifizierung](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase-Authentifizierung](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss) und vielen anderen verwenden kann. Um sie auszuprobieren, [besuche unser Beispiel-Repository](https://github.com/PlasmoHQ/examples).

## Dokumentation

Schaue dir die [Dokumentation](https://docs.plasmo.com/) an, um einen tieferen Einblick in das Plasmo Framework zu erhalten.

## Nutzung

```
pnpm dlx plasmo init example-dir
cd example-dir
pnpm dev
```

Danach stehen dir alle Wege offen.

- Popup-Änderungen kommen in `popup.tsx`
- Änderungen an der Optionsseite kommen in `options.tsx`.
- Änderungen am Inhaltsskript kommen in `content.ts`
- Änderungen am Hintergrunddienst kommen in die Datei `background.ts`.

### Ordner-Struktur

Du kannst die Dateien auch in eigenen Ordnern organisieren:

```
ext-dir
├───assets
|   └───icon512.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

Außerdem kannst du auch vermeiden, dass alle Dateien im Hauptverzeichnis liegen, wenn du sie in das Unterverzeichnis `src` legen, [indem du dieser Anleitung folgst](https://docs.plasmo.com/framework/customization/src). Beachte, dass `assets` und andere Konfigurationsdateien immer noch im Hauptverzeichnis liegen müssen.

## Community

Die Plasmo-Community ist auf [Discord](https://www.plasmo.com/s/d) zu finden. Das ist der richtige Kanal, um Hilfe bei der Verwendung des Plasmo-Frameworks zu erhalten.

Unser [Verhaltenskodex](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) gilt für alle Plasmo Community-Kanäle.

## Am Projekt beteiligen

Schaue dir die [Richtlinien](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) an, um mehr zu erfahren.

## Information

Plasmo ist derzeit eine Alphasoftware, und einige Dinge können sich von Version zu Version ändern. Sei also bitte achtsam und benutze es auf eigenes Risiko.

# Lizenz

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.fr-FR.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="License anzeigen" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Folge PlasmoHQ auf Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Schaue unsere Live DEMO jeden Freitag an" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Trete unserem Discord server bei, um zu chatten und Unterstützung für Projekte zu bekommen" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | French | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

# Plasmo Framework

Le [Plasmo](https://www.plasmo.com/) Framework est un SDK pour la création d'extensions de navigateur, développé par des hackers pour des hackers. Créez votre produit sans vous soucier des fichiers de configuration et des étranges particularités de la création d'extensions de navigateur.

> C'est comme [Next.js](https://nextjs.org/) pour les extensions de navigateur !

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## Fonctionnalités

- Prise en charge de [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/) de première classe
- [Développement déclaratif avec création automatique de "manifest.json" (MV3)](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- Chargement en temps réel
- [Content Scripts UI](https://docs.plasmo.com/csui)
- [Fichiers `.env*`](https://docs.plasmo.com/framework/env)
- [Regroupement de codes distants](https://docs.plasmo.com/framework/remote-code) (par exemple pour gtag4)
- Cibler [plusieurs paires de navigateurs et de manifestes](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- [Déploiement automatisé via BPP](https://docs.plasmo.com/framework/workflows/submit)
- [Svelte](https://github.com/PlasmoHQ/with-svelte) ou [Vue](https://github.com/PlasmoHQ/with-vue)
- Et beaucoup, beaucoup plus! 🚀

## Configuration requise

- Node.js 16.x ou plus récent
- MacOS, Windows ou Linux
- (Fortement recommandé) [pnpm](https://pnpm.io/)

## Examples

Nous avons des exemples qui montrent comment utiliser Plasmo avec [l'authentification Firebase](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss), et bien d'autres. Pour les essayer, [visitez notre référentiel d'exemples](https://github.com/PlasmoHQ/examples).

## Documentation

Consultez la [documentation](https://docs.plasmo.com/) pour obtenir une vue plus approfondie du cadre Plasmo.

## Utilisation

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

La route qui nous attend est pleine de virages.

- Les modifications de popup viennent dans `popup.tsx`.
- Les modifications de la page d'options viennent dans `options.tsx`.
- Les modifications du script de contenu se trouvent dans `content.ts`.
- Les modifications du service d'arrière-plan vont dans le fichier `background.ts`.

### Structure des dossiers

Vous pouvez également organiser ces fichiers dans leurs propres répertoires :

```
ext-dir
├───assets
|   └───icon512.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

Enfin, vous pouvez aussi éviter de placer le code source dans votre répertoire racine en le plaçant dans un sous-répertoire `src`, [en suivant ce guide](https://docs.plasmo.com/framework/customization/src). Notez que `assets` et les autres fichiers de configuration devront toujours être dans le répertoire racine.

## Communauté

La communauté Plasmo se trouve sur [Discord](https://www.plasmo.com/s/d). C'est le réseau approprié pour obtenir de l'aide sur l'utilisation du cadre Plasmo.

Notre [code de conduite](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) s'applique à tous les canaux de la communauté Plasmo.

## Contribution

Veuillez consulter les [directives de contribution](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) pour en savoir plus.

## Avis de non-responsabilité

Plasmo est actuellement un logiciel alpha, et certaines choses peuvent changer d'une version à l'autre, alors soyez attentifs et utilisez-le à vos propres risques.

# License

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.id-ID.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="See License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Follow PlasmoHQ on Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Watch our Live DEMO every Friday" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Join our Discord for support and chat about our projects" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | Indonesian | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

# Framework Plasmo

Framework [Plasmo](https://www.plasmo.com/) adalah SDK ekstensi browser penuh daya yang dibuat oleh hacker untuk hacker. Bangun produk Anda dan berhenti khawatir terhadap file konfigurasi dan berbagai macam anomali dalam membuat ekstensi browser.

> Seperti [Next.js](https://nextjs.org/) untuk ekstensi browser!

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## Fitur Utama

- First-class [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/) Support
- [Declarative Development](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- [Content Scripts UI](https://docs.plasmo.com/csui)
- [Tab Pages](https://docs.plasmo.com/framework/tab-pages)
- Live-reloading + React HMR
- [`.env*` files](https://docs.plasmo.com/framework/env)
- [Storage API](https://docs.plasmo.com/framework/storage)
- [Messaging API](https://docs.plasmo.com/framework/messaging)
- [Remote code bundling](https://docs.plasmo.com/framework/remote-code) (contohnya: untuk Google Analytics)
- Penargetan [beberapa browser dan manifest](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- [Deployment otomatis melalui BPP](https://docs.plasmo.com/framework/workflows/submit)
- Dukungan opsional untuk [Svelte](https://github.com/PlasmoHQ/with-svelte) dan [Vue](https://github.com/PlasmoHQ/with-vue)

Dan masih banyak lagi! 🚀

## Kebutuhan Sistem

- Node.js 16.x atau lebih tinggi
- MacOS, Windows, atau Linux
- (Sangat direkomendasikan) [pnpm](https://pnpm.io/)

## Examples

Kami memiliki contoh yang menunjukkan bagaimana anda dapat menggunakan Plasmo [Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss), dan banyak masih banyak lagi. Untuk mencobanya, [kunjungi repositori contoh kami](https://github.com/PlasmoHQ/examples).

## Dokumentasi

Lihat [dokumentasi](https://docs.plasmo.com/) untuk mendapatkan gambaran yang lebih mendalam mengenai Framework Plasmo.

## Browser Extensions Book

Untuk gambaran yang lebih mendalam tentang cara kerja ekstensi browser, dan cara mengembangkannya, kami sangat merekomendasikan buku baru Matt Frisbie ["Building Browser Extensions"](https://buildingbrowserextensions.com/plasmo)

## Cara Penggunaan

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

Jalan di depan dipenuhi dengan banyak pilihan.

- Mengubah Popup lakukan di `popup.tsx`
- Mengubah Options page lakukan di `options.tsx`
- Mengubah Content script lakukan di `content.ts`
- Mengubah Background service worker lakukan di `background.ts`

### Direktori

Anda juga dapat mengatur file-file ini di direktori mereka sendiri:

```
ext-dir
├───assets
|   └───icon.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

Terakhir, Anda juga dapat menghindari menempatkan kode sumber di direktori root dengan menempatkannya di sub-direktori `src`, [mengikuti panduan ini](https://docs.plasmo.com/framework/customization/src). Perhatikan bahwa `assets` dan file konfigurasi lainnya masih perlu berada di direktori root.

## Browser yang Didukung

Untuk melihat daftar target browser yang didukung, [lihat dokumentasi kami di sini](https://docs.plasmo.com/framework/workflows/faq#what-are-the-officially-supported-browser-targets).

## Komunitas

Komunitas Plasmo dapat ditemukan di [Discord](https://www.plasmo.com/s/d). Ini adalah saluran yang tepat untuk mendapatkan bantuan dalam menggunakan Plasmo Framework.

[Pedoman Perilaku](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) kami berlaku untuk semua saluran komunitas Plasmo.

## Berkontribusi

Silahkan lihat [pedoman kontribusi](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) untuk mempelajari lebih lanjut.

Terima kasih banyak untuk semua yang luar biasa [kontributor](https://github.com/PlasmoHQ/plasmo/graphs/contributors) ❤️

Jangan ragu untuk ikut bersenang-senang dan mengirim PR!

### Framework Plasmo

<a href="https://github.com/PlasmoHQ/plasmo/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/plasmo" />
</a>

### [Plasmo Examples](https://github.com/PlasmoHQ/examples)

<a href="https://github.com/PlasmoHQ/examples/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/examples" />
</a>

### [Plasmo Storage](https://github.com/PlasmoHQ/storage)

<a href="https://github.com/PlasmoHQ/storage/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/storage" />
</a>

### [Browser Platform Publisher](https://github.com/PlasmoHQ/bpp)

<a href="https://github.com/PlasmoHQ/bpp/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/bpp" />
</a>

## Disclaimer

Plasmo saat ini adalah perangkat lunak alpha, dan beberapa hal mungkin berubah dari versi ke versi, jadi harap berhati-hati dan gunakan dengan risiko Anda sendiri.

# Lisensi

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.ja-JP.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="See License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Follow PlasmoHQ on Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Watch our Live DEMO every Friday" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Join our Discord for support and chat about our projects" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | 日本語 | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

**Production Cloud:** 私たちはブラウザ拡張機能向けのクラウドサービス「Itero」を開始しました。即時のベータテストやより素晴らしい機能が必要なら、ぜひチェックしてください。

# Plasmo Framework

[Plasmo](https://www.plasmo.com/) Framework は、すべての開発者のためのブラウザ拡張機能のSDKです。拡張機能のconfigファイルやビルドにおける面倒な独自仕様に悩まされずに拡張機能を作りましょう!

> ブラウザ拡張機能における[Next.js](https://nextjs.org/)

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## 主な機能

- [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/) の全面サポート
- [宣言型開発](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- [Contents Scripts UI](https://docs.plasmo.com/csui)
- [Tab Pages](https://docs.plasmo.com/framework/tab-pages)
- ライブリロード + React HMR
- [`.env*` ファイル](https://docs.plasmo.com/framework/env)
- [Storage API](https://docs.plasmo.com/framework/storage)
- [Messaging API](https://docs.plasmo.com/framework/messaging)
- [リモートコードバンドル](https://docs.plasmo.com/framework/remote-code) (Google Analyticsなど)
- [複数ブラウザ・マニフェスト対応](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- [BPPによる自動デプロイ](https://docs.plasmo.com/framework/workflows/submit)
- [Svelte](https://github.com/PlasmoHQ/with-svelte)、 [Vue](https://github.com/PlasmoHQ/with-vue) にも対応

他にもたくさんの機能があります! 🚀

## システム要件

- Node.js 16.x 以上
- MacOS, Windows, Linux のいずれか
- [pnpm](https://pnpm.io/)(推奨)

## 例

[Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss) などと組み合わせた例を[こちらのリポジトリ](https://github.com/PlasmoHQ/examples)で紹介しています。

## ドキュメント

さらに詳しく知りたい場合は、[ドキュメント](https://docs.plasmo.com/)をご覧ください。

## ブラウザ拡張機能についての書籍

ブラウザ拡張機能の動作や開発方法についてさらに深く学びたい場合、Matt Frisbie氏の書籍[『Building Browser Extensions』](https://buildingbrowserextensions.com/plasmo)がおすすめです。

## 使い方

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

変更したい部分によって、以下のファイルを編集してください。

- ポップアップ → `popup.tsx`
- 設定ページ → `options.tsx`
- コンテンツスクリプト → `content.ts`
- バックグランドサービスワーカー → `background.ts`

### ディレクトリ構造

これらのファイルはそれぞれのディレクトリに分けて整理することもできます。

```
ext-dir
├───assets
|   └───icon.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

また、ルートディレクトリに置きたくない場合は、`src` ディレクトリを作成して、そこにソースコードを置くこともできます。詳しくは[こちらのガイド](https://docs.plasmo.com/framework/customization/src)をご覧ください。

ただし、`assets` やconfigファイルはルートディレクトリに置く必要があります。

## 対応しているブラウザ

対応しているブラウザのリストは、[こちらのドキュメント](https://docs.plasmo.com/framework/workflows/faq#what-are-the-officially-supported-browser-targets)をご覧ください。

## コミュニティ

[Discord](https://www.plasmo.com/s/d)にPlasmoのコミュニティがあります。Plasmo Framework に関するヘルプはこちらでお願いします。

[Code of Conduct](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md)は、全てのPlasmoコミュニティに適用されます。

## コントリビュート

詳しくは[コントリビュートガイドライン](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md)をご覧ください。

素晴らしい[コントリビューターの方々](https://github.com/PlasmoHQ/plasmo/graphs/contributors)に感謝します❤️

ぜひ気軽に参加してPRを送ってください!

### Plasmo Framework

<a href="https://github.com/PlasmoHQ/plasmo/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/plasmo" />
</a>

### [Plasmo Examples](https://github.com/PlasmoHQ/examples)

<a href="https://github.com/PlasmoHQ/examples/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/examples" />
</a>

### [Plasmo Storage](https://github.com/PlasmoHQ/storage)

<a href="https://github.com/PlasmoHQ/storage/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/storage" />
</a>

### [Browser Platform Publisher](https://github.com/PlasmoHQ/bpp)

<a href="https://github.com/PlasmoHQ/bpp/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/bpp" />
</a>

## 免責事項

Plasmoは現在α版のソフトウェアです。バージョンアップによって変更される可能性がありますので、ご注意いただき自己責任で使用してください。

# ライセンス

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.ko-KR.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="See License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Follow PlasmoHQ on Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Watch our Live DEMO every Friday" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Join our Discord for support and chat about our projects" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | 한국어
</p>

**Production Cloud:** [Itero](https://itero.plasmo.com)라는 브라우저 확장 프로그램용 클라우드 서비스를 구축했습니다. 즉시 베타 테스트 및 더 많은 멋진 기능을 원하시면 확인해보세요.


# Plasmo 프레임워크

[Plasmo](https://www.plasmo.com/) 프레임워크는 개발자들을 위해 만들어진 강력한 브라우저 확장 프로그램 SDK입니다. 이를 통해 제품을 만들 때 설정 파일과 브라우저 확장 프로그램 개발의 특이한 부분에 대해 걱정하지 않고 진행할 수 있습니다.

> 브라우저 확장 프로그램을 위한 [Next.js](https://nextjs.org/)와 같습니다!

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## 주요 기능

- [React](https://reactjs.org/) 및 [Typescript](https://www.typescriptlang.org/)를 위한 first-class 지원
- [선언적 개발](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- [콘텐츠 스크립트 UI](https://docs.plasmo.com/csui)
- [탭 페이지](https://docs.plasmo.com/framework/tab-pages)
- 라이브 리로딩 및 React HMR
- [`.env*` 파일](https://docs.plasmo.com/framework/env)
- [Storage API](https://docs.plasmo.com/framework/storage)
- [Messaging API](https://docs.plasmo.com/framework/messaging)
- [원격 코드 번들링](https://docs.plasmo.com/framework/remote-code) (ex. Google Analytics를 위한)
- [여러 브라우저 및 매니페스트 타겟팅](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- [BPP를 통한 자동 배포](https://docs.plasmo.com/framework/workflows/submit)
- [Svelte](https://github.com/PlasmoHQ/with-svelte) 및 [Vue](https://github.com/PlasmoHQ/with-vue)의 선택적 지원

이 외에도 많은 기능이 있습니다! 🚀

## 시스템 요구 사항

- Node.js 16.x 이상
- MacOS, Windows 또는 Linux
- (매우 권장) [pnpm](https://pnpm.io/)

## 예제

[Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss)와 함께 Plasmo를 사용하는 방법을 보여주는 예제를 제공하고 있습니다. 이를 확인하려면 [예제 레포지토리](https://github.com/PlasmoHQ/examples)를 방문해보세요.

## 문서

Plasmo 프레임워크에 대해 더 자세히 알아보려면 [문서](https://docs.plasmo.com/)를 확인하세요.


## 브라우저 확장 프로그램 관련 책

브라우저 확장 프로그램이 작동하는 방식과 개발하는 방법에 대해 더 자세히 알아보려면 Matt Frisbie의 새 책 ["Building Browser Extensions"](https://buildingbrowserextensions.com/plasmo)을 강력히 추천합니다.

## 사용법

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

변경하고자 하는 부분에 따라 아래 파일을 편집해 주세요.

- 팝업 → `popup.tsx` 파일
- 옵션 페이지 → `options.tsx`
- 콘텐츠 스크립트 → `content.ts`
- 백그라운드 서비스 워커 → `background.ts`

### 폴더 구조

이 파일들을 각각의 디렉토리에 정리해서 넣을 수도 있습니다.

```
ext-dir
├───assets
|   └───icon.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

마지막으로, 소스 코드를 루트 디렉토리에 넣지 않고 [이 가이드를 따라](https://docs.plasmo.com/framework/customization/src) `src` 하위 디렉토리에 넣을 수도 있습니다. 그러나 `assets` 및 기타 구성 파일은 여전히 루트 디렉토리에 있어야 합니다.

## 지원하는 브라우저

지원하는 브라우저 목록을 확인하려면 [이 문서](https://docs.plasmo.com/framework/workflows/faq#what-are-the-officially-supported-browser-targets)를 참조하세요.

## 커뮤니티

Plasmo 커뮤니티는 [Discord](https://www.plasmo.com/s/d)에서 찾을 수 있습니다. Plasmo 프레임워크 사용에 관한 도움을 받기에 적절한 채널입니다.

[행동 강령(Code of Conduct)](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md)은 모든 Plasmo 커뮤니티 채널에 적용됩니다.

## 기여

자세한 내용은 [기여 가이드라인(Contributing Guidelines)](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md)을 참조하세요.

훌륭한 [컨트리뷰터](https://github.com/PlasmoHQ/plasmo/graphs/contributors) 여러분들께 큰 감사를 드립니다 ❤️

자유롭게 참여하고 PR을 보내주세요!

### Plasmo Framework

<a href="https://github.com/PlasmoHQ/plasmo/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/plasmo" />
</a>

### [Plasmo Examples](https://github.com/PlasmoHQ/examples)

<a href="https://github.com/PlasmoHQ/examples/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/examples" />
</a>

### [Plasmo Storage](https://github.com/PlasmoHQ/storage)

<a href="https://github.com/PlasmoHQ/storage/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/storage" />
</a>

### [Browser Platform Publisher](https://github.com/PlasmoHQ/bpp)

<a href="https://github.com/PlasmoHQ/bpp/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/bpp" />
</a>

## 면책 조항

Plasmo는 현재 알파 버전의 소프트웨어이며, 버전 간에 일부 변경 사항이 있을 수 있으므로 주의하고 사용하십시오.

# 라이선스

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.ru-RU.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="See License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Follow PlasmoHQ on Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Watch our Live DEMO every Friday" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Join our Discord for support and chat about our projects" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | Русский | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

# Plasmo Framework

Фреймворк [Plasmo](https://www.plasmo.com/) это SDK для разработки кроссплатформерных расширений для браузера, созданное хакерами для хакеров. Разрабатывайте расширения и перестаньте беспокоится о конфигах и специфичных особенностях браузерных расширений.

> Это как [Next.js](https://nextjs.org/) для браузерных расширений!

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## Главные особенности

- Первоклассная поддержка [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/)
- [Декларативная настройка "manifest.json" (MV3)](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- [Контент скрипты с поддержкой UI](https://docs.plasmo.com/csui)
- [Вкладки расширения](https://docs.plasmo.com/framework/tab-pages)
- Активная перезагрузка + React HMR
- [`.env*` файлы](https://docs.plasmo.com/framework/env)
- [API для хранения информации](https://docs.plasmo.com/framework/storage)
- [API для общения между различными частями расширения](https://docs.plasmo.com/framework/messaging)
- [Подключение удаленного кода](https://docs.plasmo.com/framework/remote-code) (e.g., for Google Analytics)
- Поддержка [кроссплатфоменности и различных видов манифеста](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- [Автоматическое развертывание с помощью BPP](https://docs.plasmo.com/framework/workflows/submit)
- Дополнительная поддержка [Svelte](https://github.com/PlasmoHQ/with-svelte) и [Vue](https://github.com/PlasmoHQ/with-vue)

И многое, многое другое! 🚀

## Системные требования

- Node.js 16.x или выше
- MacOS, Windows, или Linux
- (Настоятельно рекомендуется) [pnpm](https://pnpm.io/)

## Примеры

У нас есть примеры, показывающие, как можно использовать Plasmo с [Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss), и многое другое. Чтобы посмотреть, [посетите наш репозиторий примеров](https://github.com/PlasmoHQ/examples).

## Документация

Ознакомьтесь с [documentation](https://docs.plasmo.com/) чтобы получить более глубокое представление о Plasmo Framework.

## Книга расширений браузера

Для более подробного ознакомления с тем, как работают расширения браузера и как их разрабатывать, мы настоятельно рекомендуем новую книгу Мэтта Фрисби ["Building Browser Extensions"](https://buildingbrowserextensions.com/plasmo)

## Использование

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

Дальнейший путь наполнен возможностями.

- Изменение Popup в `popup.tsx`
- Редактирование страницы настроек расширения в `options.tsx`
- Настройка контент скриптов в `content.ts`
- Изменение Background service worker в `background.ts`

### Каталоги

Вы также можете структурировать эти файлы в собственных каталогах:

```
папка-расширения
├───assets
|   └───icon.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

Наконец, вы также можете избежать размещения исходного кода в вашем корневом каталоге, поместив их в подкаталог `src`, [следуя этому руководству](https://docs.plasmo.com/framework/customization/src). Обратите внимание что `assets` и другие конфигурационные файлы по-прежнему должны находиться в корневом каталоге.

## Поддерживаемые браузеры

Чтобы просмотреть список поддерживаемых браузеров, [пожалуйста, обратитесь к нашей документации здесь](https://docs.plasmo.com/framework/workflows/faq#what-are-the-officially-supported-browser-targets).

## Сообщество

Сообщество Plasmo можно найти в [Discord](https://www.plasmo.com/s/d). Это подходящий канал для получения помощи в использовании Plasmo Framework.

Наш [Кодекс поведения](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) применяется ко всем каналам сообщества Plasmo.

## Внести свой вклад

Пожалуйста, ознакомьтесь с [рекомендациями по контрибьютингу](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) чтобы узнать больше.

Большое спасибо всем нашим удивительным [помощникам](https://github.com/PlasmoHQ/plasmo/graphs/contributors) ❤️

Не стесняйтесь присоединиться к веселью и отправить PR!

### Plasmo Framework

<a href="https://github.com/PlasmoHQ/plasmo/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/plasmo" />
</a>

### [Примеры Plasmo](https://github.com/PlasmoHQ/examples)

<a href="https://github.com/PlasmoHQ/examples/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/examples" />
</a>

### [Plasmo Storage](https://github.com/PlasmoHQ/storage)

<a href="https://github.com/PlasmoHQ/storage/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/storage" />
</a>

### [Browser Platform Publisher](https://github.com/PlasmoHQ/bpp)

<a href="https://github.com/PlasmoHQ/bpp/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/bpp" />
</a>

## Дисклеймер

В настоящее время Plasmo является альфа-версией программного обеспечения, и некоторые вещи могут меняться от версии к версии, поэтому, пожалуйста, будьте внимательны и используйте его на свой страх и риск.

# Лицензия

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.tr-TR.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="See License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Follow PlasmoHQ on Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Watch our Live DEMO every Friday" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Join our Discord for support and chat about our projects" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | Turkish | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

# Plasmo Framework

[Plasmo](https://www.plasmo.com/) Framework, hacker ruhlu yazılımcılar tarafından hacker ruhlu yazılımcılar için yapılmış pille dolu bir tarayıcı uzantısı geliştirme kiti'dir.

> Tarayıcı uzantılarının [Next.js](https://nextjs.org/)'i gibi.

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## Öne Çıkan Özellikler

- First-class [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/) Desteği
- [Declarative Geliştirme](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- [Content Scripts UI](https://docs.plasmo.com/csui)
- [Sekme Sayfaları](https://docs.plasmo.com/framework/tab-pages)
- Canlı-reloading + React HMR
- [`.env*` dosyaları](https://docs.plasmo.com/framework/env)
- [Storage API'ı](https://docs.plasmo.com/framework/storage)
- [Messaging API'ı](https://docs.plasmo.com/framework/messaging)
- [Remote code bundle'lama](https://docs.plasmo.com/framework/remote-code) (örn: Google Analytics için)
- [Birden çok tarayıcı ve manifest eşi](https://docs.plasmo.com/framework/workflows/build#with-specific-target) hedefleme
- [BPP ile otomatik deploy](https://docs.plasmo.com/framework/workflows/submit)
- İsteğe bağlı [Svelte](https://github.com/PlasmoHQ/with-svelte) ve [Vue](https://github.com/PlasmoHQ/with-vue) desteği

Ve daha, daha fazlası! 🚀

## Sistem Gereksinimleri

- Node.js 16.x ve üzeri
- MacOS, Windows veya Linux
- (Şiddetle Tavsiye) [pnpm](https://pnpm.io/)

## Örnekler

Plasmo'nun [Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss) ve çok daha fazlası ile nasıl kullanılabileceğini gösteren örneklerimiz mevcut. Bunları görmek için [örnekler repomuzu ziyaret edin](https://github.com/PlasmoHQ/examples).

## Dökümantasyon

Plasmo Framework'u hakkında daha derinlemesine bilgi edinmek için [dökümantasyon](https://docs.plasmo.com/)'a göz atın.

## Tarayıcı Uzantıları Kitabı

Tarayıcı uzantılarının nasıl çalıştığına ve nasıl geliştirileceğine dair daha derinlemesine bir bakış için Matt Frisbie'nin yeni kitabı "[Building Browser Extensions](https://buildingbrowserextensions.com/plasmo)"ı şiddetle tavsiye ediyoruz.

## Kullanım

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

Önümüzdeki yol birçok virajla dolu.

- Popup değişiklikleri `popup.tsx` dosyasına eklenir
- Seçenekler sayfası değişiklikleri `options.tsx` dosyasına eklenir
- Content script değişiklikleri `content.ts` dosyasına eklenir
- Arka plan hizmet çalışanı değişiklikleri `background.ts` dosyasına eklenir

### Dizinler

Bu dosyaları kendi dizinlerine sahip olacak şekilde de düzenleyebilirsiniz:

```
ext-dir
├───assets
|   └───icon.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

Son olarak, kaynak kodunu kök dizinine koymak yerine `src` alt dizinine koymak için [bu kılavuzu izleyebilirsin](https://docs.plasmo.com/framework/customization/src). `assets`'lerinizin ve diğer config dosyalarının yine de kök dizininde olması gerekeceğini unutmayın.

## Desteklenen Tarayıcılar

Desteklenen tarayıcı hedeflerinin bir listesini görmek için [lütfen buradaki dökümantasyon'a bakın](https://docs.plasmo.com/framework/workflows/faq#what-are-the-officially-supported-browser-targets).

## Topluluk

Plasmo topluluğu [Discord](https://www.plasmo.com/s/d)'da. Bu Plasmo Framework'ü kullanma konusunda yardım almak için uygun bir kanaldır.

[Davranış Kurallarımız](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) tüm Plasmo topluluk kanalları için geçerlidir.

## Katkıda bulunma

Daha fazla bilgi edinmek için lütfen [katkıda bulunma yönergelerine](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) bakın.

Katkıda bulunan tüm harika [katılımcılarımıza](https://github.com/PlasmoHQ/plasmo/graphs/contributors) çok teşekkür ederiz ❤️

Eğlenceye katılmaktan ve PR göndermekten çekinmeyin!

### Plasmo Framework

<a href="https://github.com/PlasmoHQ/plasmo/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/plasmo" />
</a>

### [Plasmo Örnekleri](https://github.com/PlasmoHQ/examples)

<a href="https://github.com/PlasmoHQ/examples/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/examples" />
</a>

### [Plasmo Storage](https://github.com/PlasmoHQ/storage)

<a href="https://github.com/PlasmoHQ/storage/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/storage" />
</a>

### [Browser Platform Publisher](https://github.com/PlasmoHQ/bpp)

<a href="https://github.com/PlasmoHQ/bpp/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/bpp" />
</a>

## Sorumluluk Reddi

Plasmo şu anda alfa yazılımıdır ve bazı şeyler sürümden sürüme değişebilir, bu nedenle lütfen dikkatli olun ve riski size ait olacak şekilde kullanın.

# Lisans

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.vi-VN.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="Xem License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Theo dõi PlasmoHQ trên Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Xem trực tiếp DEMO mỗi thứ Sáu" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Tham gia Discord để chat về Plasmo" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | <a href="/cli/plasmo/i18n/README.zh-CN.md">简体中文</a> | Tiếng Việt | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

# Plasmo Framework

[Plasmo](https://www.plasmo.com/) là một framework dùng để xây dựng ứng dụng mở rộng cho trình duyệt web (browser extension) với nhiều tính năng tối ưu hóa, tạo bởi hackers cho hackers. Xây dựng sản phẩm mà không phải lo lắng về config và những dị thù khi làm việc với extension.

> Giống như [Next.js](https://nextjs.org/) cho extension!

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## Tính năng

- [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/)
- [Tự động hóa `manifest.json` với MV3](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- Tự động reload trình duyệt
- [`.env*` file](https://docs.plasmo.com/framework/env)
- [Content Scripts UI](https://docs.plasmo.com/csui)
- [Gói mã nguồn online](https://docs.plasmo.com/framework/workflows/remote-code) (e.g for gtag4)
- [Tự động xuất bản với BPP](https://docs.plasmo.com/framework/workflows/submit)
- [Tạo extension cho mọi trình duyệt](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- Dùng với [Svelte](https://github.com/PlasmoHQ/with-svelte) hoặc [Vue](https://github.com/PlasmoHQ/with-vue)
- Và nhiều hơn nữa! 🚀

## Yêu cầu hệ thống

- Node.js 16.x trở lên
- MacOS, Windows, hoặc Linux
- (Khuyến khích) [pnpm](https://pnpm.io/)

## Ví dụ

Chúng tôi có các ví dụ giới thiệu cách bạn có thể sử dụng Plasmo với [Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss), và nhiều hơn nữa. Để xem chúng, hãy [truy cập kho ví dụ của chúng tôi](https://github.com/PlasmoHQ/examples).

## Tài liệu

Xem [tài liệu](https://docs.plasmo.com/) để nhìn chuyên sâu hơn.

## Cách sử dụng

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

Con đường phía trước còn nhiều trông gai.

- Thay đổi popup trong `popup.tsx`
- Thay đổi trang Options trong `options.tsx`
- Thay đổi Content script trong `content.ts`
- Thay đổi dịch vụ nền (Background service worker) trong `background.ts`

### Thư mục

Bạn có thể sắp xếp các tệp này trong thư mục riêng của chúng:

```
ext-dir
├───assets
|   └───icon512.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

Cuối cùng, bạn cũng có thể tránh đặt mã nguồn vào thư mục gốc của mình bằng cách đặt chúng vào thư mục con `src`, [làm theo hướng dẫn này](https://docs.plasmo.com/framework/customization/src). Lưu ý, thư mục `assets` và các tệp config vẫn cần phải ở trong thư mục gốc.

## Cộng đồng

Cộng đồng Plasmo có thể được tìm thấy trên [Discord](https://www.plasmo.com/s/d). Đây là kênh thích hợp để nhận trợ giúp về việc sử dụng Plasmo Framework.

[Quy tắc ứng xử](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) của chúng tôi áp dụng cho tất cả các kênh cộng đồng của Plasmo.

## Đóng góp

Vui lòng xem [hướng dẫn đóng góp](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) để tìm hiểu thêm.

## Tuyên bố từ chối trách nhiệm

Plasmo hiện là phần mềm alpha và một số thứ có thể thay đổi từ phiên bản này sang phiên bản khác. Xin lưu ý, Plasmo sẽ không chịu trách nhiệm nếu bạn gặp rủi ro khi xử dụng phần mềm này.

# Giấy phép bản quyền

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/i18n/README.zh-CN.md
================================================
<p align="center">
  <a href="https://plasmo.com">
    <img alt="plasmo logo" width="75%" src="https://www.plasmo.com/assets/banner-black-on-white.png" />
  </a>
</p>

<p align="center">
  <a aria-label="License" href="/cli/plasmo/LICENSE">
    <img alt="See License" src="https://img.shields.io/npm/l/plasmo"/>
  </a>
  <a aria-label="NPM" href="https://www.npmjs.com/package/plasmo">
    <img alt="NPM Install" src="https://img.shields.io/npm/v/plasmo?logo=npm"/>
  </a>
  <a aria-label="Twitter" href="https://www.twitter.com/plasmohq">
    <img alt="Follow PlasmoHQ on Twitter" src="https://img.shields.io/twitter/follow/plasmohq?logo=twitter"/>
  </a>
  <a aria-label="Twitch Stream" href="https://www.twitch.tv/plasmohq">
    <img alt="Watch our Live DEMO every Friday" src="https://img.shields.io/twitch/status/plasmohq?logo=twitch&logoColor=white"/>
  </a>
  <a aria-label="Discord" href="https://www.plasmo.com/s/d">
    <img alt="Join our Discord for support and chat about our projects" src="https://img.shields.io/discord/946290204443025438?logo=discord&logoColor=white"/>
  </a>
</p>

<p align="center">
  <a href="/cli/plasmo/README.md">English</a> | 简体中文 | <a href="/cli/plasmo/i18n/README.vi-VN.md">Tiếng Việt</a> | <a href="/cli/plasmo/i18n/README.de-DE.md">Deutsch</a> | <a href="/cli/plasmo/i18n/README.fr-FR.md">French</a> | <a href="/cli/plasmo/i18n/README.id-ID.md">Indonesian</a> | <a href="/cli/plasmo/i18n/README.ru-RU.md">Русский</a> | <a href="/cli/plasmo/i18n/README.tr-TR.md">Turkish</a> | <a href="/cli/plasmo/i18n/README.ja-JP.md">日本語</a> | <a href="/cli/plasmo/i18n/README.ko-KR.md">한국어</a>
</p>

**云服务:** 我们为浏览器扩展程序构建了一个云服务,叫 [Itero](https://itero.plasmo.com) 。如果你想要进行即时beta测试并体验更多很棒的功能,可以去尝试一下。

# Plasmo 框架

[Plasmo](https://www.plasmo.com/) 框架是一款黑客为黑客打造的功能强大的浏览器扩展程序软件开发工具包(SDK)。使用 Plasmo 来构建你的浏览器扩展程序,不需要操心扩展的配置文件和构建时的一些奇怪特性。

> 它就像浏览器扩展界的 [Next.js](https://nextjs.org/) !

![CLI Demo](https://www.plasmo.com/assets/plasmo-cli-demo.gif)

## 特性

- 一流的 [React](https://reactjs.org/) + [Typescript](https://www.typescriptlang.org/) 支持
- [声明式开发(自动生成 manifest.json)](https://docs.plasmo.com/framework#where-is-the-manifestjson-file)
- [将UI组件渲染到网页](https://docs.plasmo.com/csui)
- [扩展内置页面](https://docs.plasmo.com/framework/tab-pages)
- 扩展热重载 + React 模块热更新
- [`.env*` 文件](https://docs.plasmo.com/framework/env)
- [扩展储存 API](https://docs.plasmo.com/framework/storage)
- [扩展通信 API](https://docs.plasmo.com/framework/messaging)
- [远程代码打包](https://docs.plasmo.com/framework/remote-code) (例如 Google Analytics)
- 支持[多个浏览器和manifest版本](https://docs.plasmo.com/framework/workflows/build#with-specific-target)
- [通过BPP进行自动部署](https://docs.plasmo.com/framework/workflows/submit)
- 可选 [Svelte](https://github.com/PlasmoHQ/with-svelte) 或 [Vue](https://github.com/PlasmoHQ/with-vue) 进行开发

还有更多的功能!🚀

## 系统要求

- Node.js 16.x 及以上
- MacOS,Windows,或 Linux
- (强烈推荐) [pnpm](https://pnpm.io/)

## 代码示例

我们有一些展示如何集成 [Firebase Authentication](https://github.com/PlasmoHQ/examples/tree/main/with-firebase-auth), [Redux](https://github.com/PlasmoHQ/examples/tree/main/with-redux), [Supabase authentication](https://github.com/PlasmoHQ/examples/tree/main/with-supabase), [Tailwind](https://github.com/PlasmoHQ/examples/tree/main/with-tailwindcss) 以及更多技术的代码示例。如果想要浏览全部代码示例,请[访问示例仓库](https://github.com/PlasmoHQ/examples)。

## 文档

阅读 [文档](https://docs.plasmo.com/) 以更深入地了解 Plasmo 框架。

## 浏览器扩展书籍

为了更深入了解浏览器扩展工作原理和开发方法,我们强烈推荐 Matt Frisbie 的新书 ["Building Browser Extensions"](https://buildingbrowserextensions.com/plasmo)。

## 使用

```
pnpm create plasmo example-dir
cd example-dir
pnpm dev
```

注意

- Popup 页面改动应在 `popup.tsx`
- Options 页面改动应在 `options.tsx`
- Content script 改动应在 `content.ts`
- Background service worker 改动应在 `background.ts`

### 目录

您还可以在它们各自的目录中组织这些文件:

```
ext-dir
├───assets
|   └───icon.png
├───popup
|   ├───index.tsx
|   └───button.tsx
├───options
|   ├───index.tsx
|   ├───utils.ts
|   └───input.tsx
├───contents
|   ├───site-one.ts
|   ├───site-two.ts
|   └───site-three.ts
...
```

此外,您也能够将代码放到 `src` 子目录,而不将它们放到根目录,请[参阅该指南](https://docs.plasmo.com/framework/customization/src)。注意 `assets` 和其他配置文件仍须在根目录下。

## 支持的浏览器

要查看支持的浏览器列表,[请参考我们此处的文档](https://docs.plasmo.com/framework/workflows/faq#what-are-the-officially-supported-browser-targets).

## 社区

可以在 [Discord](https://www.plasmo.com/s/d) 找到 Plasmo 社区。这是获得 Plasmo 框架使用帮助的恰当渠道。

我们的 [行为守则](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CODE_OF_CONDUCT.md) 适用于所有 Plasmo 社区频道。

## 贡献

请参阅 [贡献指南](https://github.com/PlasmoHQ/plasmo/blob/main/.github/CONTRIBUTING.md) 以了解更多内容。

非常感谢所有的 [贡献者](https://github.com/PlasmoHQ/plasmo/graphs/contributors) ❤️

欢迎发送PR加入我们的行列!

### Plasmo Framework

<a href="https://github.com/PlasmoHQ/plasmo/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/plasmo" />
</a>

### [Plasmo Examples](https://github.com/PlasmoHQ/examples)

<a href="https://github.com/PlasmoHQ/examples/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/examples" />
</a>

### [Plasmo Storage](https://github.com/PlasmoHQ/storage)

<a href="https://github.com/PlasmoHQ/storage/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/storage" />
</a>

### [Browser Platform Publisher](https://github.com/PlasmoHQ/bpp)

<a href="https://github.com/PlasmoHQ/bpp/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=PlasmoHQ/bpp" />
</a>

## 免责声明

Plasmo 当前仍为 alpha 软件,且不同版本间可能存在修改,所以在使用过程中请留意,风险自负。

# 协议

[MIT](https://github.com/PlasmoHQ/plasmo/blob/main/LICENSE) ⭐ [Plasmo](https://www.plasmo.com)


================================================
FILE: cli/plasmo/index.mjs
================================================
import { argv, exit } from "process"
import { build, context } from "esbuild"
import fse from "fs-extra"

const watch = argv.includes("-w")

/** @type import('esbuild').BuildOptions */
const commonConfig = {
  sourcemap: watch ? "inline" : false,
  minify: !watch,
  logLevel: watch ? "info" : "warning",

  bundle: true
}

async function main() {
  const config = await fse.readJson("package.json")
  const define = {
    "process.env.APP_VERSION": `"${config.version}"`
  }

  /** @type import('esbuild').BuildOptions */
  const opts = {
    ...commonConfig,
    entryPoints: ["src/index.ts"],
    external: Object.keys(config.dependencies),
    platform: "node",
    format: "esm",
    define,
    banner: {
      js: "import { createRequire } from 'module';const require = createRequire(import.meta.url);"
    },
    outfile: "dist/index.js"
  }

  if (watch) {
    const ctx = await context(opts)
    await ctx.watch()
  } else {
    await build(opts)
  }
}

main()

process.on("SIGINT", () => exit(0))
process.on("SIGTERM", () => exit(0))


================================================
FILE: cli/plasmo/package.json
================================================
{
  "name": "plasmo",
  "version": "0.90.5",
  "description": "The Plasmo Framework CLI",
  "publishConfig": {
    "types": "dist/type.d.ts"
  },
  "types": "src/type.ts",
  "main": "dist/index.js",
  "bin": "bin/index.mjs",
  "type": "module",
  "files": [
    "bin/index.mjs",
    "dist/index.js",
    "dist/type.d.ts",
    "templates"
  ],
  "scripts": {
    "dev": "node index.mjs -w",
    "build": "node index.mjs",
    "type": "tsup src/type.ts --format esm --dts-only --dts-resolve",
    "prepublishOnly": "run-p type build",
    "lint": "run-p lint:*",
    "lint:type": "tsc --noemit",
    "lint:code": "eslint src/**/*.ts"
  },
  "author": "Plasmo Corp. <support@plasmo.com>",
  "homepage": "https://docs.plasmo.com/",
  "repository": {
    "type": "git",
    "url": "https://github.com/PlasmoHQ/plasmo.git",
    "directory": "cli/plasmo"
  },
  "license": "MIT",
  "keywords": [
    "plasmo",
    "browser-extensions",
    "framework"
  ],
  "dependencies": {
    "@expo/spawn-async": "1.7.2",
    "@parcel/core": "2.9.3",
    "@parcel/fs": "2.9.3",
    "@parcel/package-manager": "2.9.3",
    "@parcel/watcher": "2.5.1",
    "@plasmohq/init": "workspace:*",
    "@plasmohq/parcel-config": "workspace:*",
    "@plasmohq/parcel-core": "workspace:*",
    "buffer": "6.0.3",
    "chalk": "5.4.1",
    "change-case": "5.4.4",
    "dotenv": "16.4.7",
    "dotenv-expand": "12.0.1",
    "events": "3.3.0",
    "fast-glob": "3.3.3",
    "fflate": "0.8.2",
    "get-port": "7.1.0",
    "got": "14.4.6",
    "ignore": "7.0.3",
    "inquirer": "12.5.0",
    "is-path-inside": "4.0.0",
    "json5": "2.2.3",
    "mnemonic-id": "3.2.7",
    "node-object-hash": "3.1.1",
    "package-json": "10.0.1",
    "process": "0.11.10",
    "semver": "7.7.1",
    "sharp": "0.33.5",
    "tempy": "3.1.0",
    "typescript": "5.8.2"
  },
  "devDependencies": {
    "@plasmo/config": "workspace:*",
    "@plasmo/constants": "workspace:*",
    "@plasmo/framework-shared": "workspace:*",
    "@plasmo/utils": "workspace:*",
    "vue": "3.5.13"
  }
}


================================================
FILE: cli/plasmo/src/commands/build.ts
================================================
import { getNonFlagArgvs } from "@plasmo/utils/argv"
import { hasFlag } from "@plasmo/utils/flags"
import { iLog, sLog } from "@plasmo/utils/logging"

import { getBundleConfig } from "~features/extension-devtools/get-bundle-config"
import { nextNewTab } from "~features/extra/next-new-tab"
import { checkNewVersion } from "~features/framework-update/version-tracker"
import { createParcelBuilder } from "~features/helpers/create-parcel-bundler"
import { printHeader } from "~features/helpers/print"
import { createManifest } from "~features/manifest-factory/create-manifest"
import { zipBundle } from "~features/manifest-factory/zip"

async function build() {
  printHeader()
  checkNewVersion()

  process.env.NODE_ENV = "production"

  const [internalCmd] = getNonFlagArgvs("build")

  if (internalCmd === "next-new-tab") {
    await nextNewTab()
    return
  }

  iLog("Prepare to bundle the extension...")

  const bundleConfig = getBundleConfig()

  iLog("Building for target:", bundleConfig.target)

  const plasmoManifest = await createManifest(bundleConfig)

  const bundler = await createParcelBuilder(plasmoManifest, {
    mode: "production",
    shouldDisableCache: true,
    shouldContentHash: false,
    defaultTargetOptions: {
      shouldOptimize: true,
      shouldScopeHoist: hasFlag("--hoist")
    }
  })

  const result = await bundler.run()
  sLog(`Finished in ${result.buildTime}ms!`)

  await plasmoManifest.postBuild()

  if (hasFlag("--zip")) {
    await zipBundle(plasmoManifest.commonPath)
  }
}

export default build


================================================
FILE: cli/plasmo/src/commands/dev.ts
================================================
import {
  BuildSocketEvent,
  getBuildSocket
} from "@plasmo/framework-shared/build-socket"
import { getFlag, isVerbose } from "@plasmo/utils/flags"
import { eLog, iLog, sLog, vLog } from "@plasmo/utils/logging"

import { getBundleConfig } from "~features/extension-devtools/get-bundle-config"
import { createProjectWatcher } from "~features/extension-devtools/project-watcher"
import { checkNewVersion } from "~features/framework-update/version-tracker"
import { createParcelBuilder } from "~features/helpers/create-parcel-bundler"
import { startLoading, stopLoading } from "~features/helpers/loading-animation"
import { printHeader } from "~features/helpers/print"
import { createManifest } from "~features/manifest-factory/create-manifest"

async function dev() {
  printHeader()
  checkNewVersion()

  process.env.NODE_ENV = "development"

  const rawServePort = getFlag("--serve-port") || "1012"
  const serveHost = getFlag("--serve-host") || "localhost"
  const rawHmrPort = getFlag("--hmr-port") || "1815"
  const hmrHost = getFlag("--hmr-host") || "localhost"

  iLog("Starting the extension development server...")

  const { default: getPort } = await import("get-port")

  const [servePort, hmrPort] = await Promise.all([
    getPort({ port: parseInt(rawServePort) }),
    getPort({ port: parseInt(rawHmrPort) })
  ])

  const buildWatcher = getBuildSocket(hmrHost, hmrPort)
  vLog(
    `Starting dev server on ${serveHost}:${servePort}, HMR on ${hmrHost}:${hmrPort}...`
  )

  const bundleConfig = getBundleConfig()

  iLog("Building for target:", bundleConfig.target)

  const plasmoManifest = await createManifest(bundleConfig)

  const projectWatcher = await createProjectWatcher(plasmoManifest)

  const bundler = await createParcelBuilder(plasmoManifest, {
    logLevel: "verbose",
    shouldBundleIncrementally: true,
    serveOptions: {
      host: serveHost,
      port: servePort
    },
    hmrOptions: {
      host: hmrHost,
      port: hmrPort
    }
  })

  const { default: chalk } = await import("chalk")

  const bundlerWatcher = await bundler.watch(async (err, event) => {
    if (err) {
      stopLoading()
      throw err
    }

    if (event === undefined) {
      return
    }

    if (event.type === "buildStart") {
      startLoading()
      return
    }

    if (event.type === "buildSuccess") {
      stopLoading()
      sLog(`Extension re-packaged in ${chalk.bold(event.buildTime)}ms! 🚀`)
      await plasmoManifest.postBuild()
      buildWatcher.broadcast(BuildSocketEvent.BuildReady)
      return
    }

    if (event.type === "buildFailure") {
      stopLoading()
      if (!isVerbose()) {
        eLog(
          chalk.redBright(
            `Build failed. To debug, run ${chalk.bold("plasmo dev --verbose")}.`
          )
        )
      }
      event.diagnostics.forEach((diagnostic) => {
        eLog(chalk.redBright(diagnostic.message))
        if (diagnostic.stack) {
          vLog(diagnostic.stack)
        }

        diagnostic.hints?.forEach((hint) => {
          vLog(hint)
        })

        diagnostic.codeFrames?.forEach((codeFrame) => {
          if (codeFrame.code) {
            vLog(codeFrame.code)
          }
          codeFrame.codeHighlights.forEach((codeHighlight) => {
            if (codeHighlight.message) {
              vLog(codeHighlight.message)
            }

            vLog(
              chalk.underline(
                `${codeFrame.filePath}:${codeHighlight.start.line}:${codeHighlight.start.column}`
              )
            )
          })
        })
      })
    }
    process.env.__PLASMO_FRAMEWORK_INTERNAL_WATCHER_STARTED = "true"
  })

  const cleanup = () => {
    projectWatcher?.unsubscribe()
    bundlerWatcher.unsubscribe()
  }

  process.on("SIGINT", cleanup)
  process.on("SIGTERM", cleanup)
}

export default dev


================================================
FILE: cli/plasmo/src/commands/help.ts
================================================
import { printHeader, printHelp } from "~features/helpers/print"

async function help() {
  printHeader()
  printHelp()
}

export default help


================================================
FILE: cli/plasmo/src/commands/index.ts
================================================
export const runMap = {
  help: () => import("./help"),

  //#ifdef !IS_BINARY
  start: () => import("./start"),
  init: () => import("./init"),
  dev: () => import("./dev"),
  build: () => import("./build"),
  package: () => import("./package"),
  //#endif

  version: () => import("./version"),
  ["-v"]: () => import("./version"),
  ["--version"]: () => import("./version")
}

export type ValidCommand = keyof typeof runMap

export const validCommandList = Object.keys(runMap) as ValidCommand[]

export const validCommandSet = new Set(validCommandList)


================================================
FILE: cli/plasmo/src/commands/init.ts
================================================
import { resolve } from "path"
import { cwd } from "process"
import { kebabCase } from "change-case"

import { hasFlag } from "@plasmo/utils/flags"
import { ensureWritableAndEmpty } from "@plasmo/utils/fs"
import { vLog } from "@plasmo/utils/logging"

import { getCommonPath } from "~features/extension-devtools/common-path"
import { getPackageManager } from "~features/helpers/package-manager"
import { printHeader } from "~features/helpers/print"
import { ProjectCreator } from "~features/project-creator"
import { getRawName } from "~features/project-creator/get-raw-name"
import { gitInit } from "~features/project-creator/git-init"
import { installDependencies } from "~features/project-creator/install-dependencies"
import { printReady } from "~features/project-creator/print-ready"

async function init() {
  printHeader()

  const isExample = hasFlag("--exp")
  const rawName = await getRawName()

  const currentDirectory = cwd()

  // For resolving project directory
  const projectDirectory = resolve(
    currentDirectory,
    kebabCase(rawName) || rawName
  )

  vLog("Project directory:", projectDirectory)

  const commonPath = getCommonPath(projectDirectory)

  vLog("Package name:", commonPath.packageName)

  if (isExample && !commonPath.packageName.startsWith("with-")) {
    throw new Error("Example extensions must have the `with-` prefix")
  }

  await ensureWritableAndEmpty(projectDirectory)

  const packageManager = await getPackageManager()
  vLog(
    `Using package manager: ${packageManager.name} ${packageManager?.version}`
  )

  const creator = new ProjectCreator(commonPath, packageManager, isExample)
  await creator.create()

  await installDependencies(projectDirectory, packageManager)

  await gitInit(commonPath, projectDirectory)

  await printReady(
    projectDirectory,
    currentDirectory,
    commonPath,
    packageManager
  )
}

export default init


================================================
FILE: cli/plasmo/src/commands/package.ts
================================================
import { hasFlag } from "@plasmo/utils/flags"
import { iLog } from "@plasmo/utils/logging"

import { getBundleConfig } from "~features/extension-devtools/get-bundle-config"
import { checkNewVersion } from "~features/framework-update/version-tracker"
import { printHeader } from "~features/helpers/print"
import { createManifest } from "~features/manifest-factory/create-manifest"
import { zipBundle } from "~features/manifest-factory/zip"

async function packageCmd() {
  printHeader()
  checkNewVersion()

  process.env.NODE_ENV = "production"

  iLog("Prepare to package the extension bundle...")

  const bundleConfig = getBundleConfig()

  const plasmoManifest = await createManifest(bundleConfig)

  await zipBundle(plasmoManifest.commonPath, hasFlag("--with-source-maps"))
}

export default packageCmd


================================================
FILE: cli/plasmo/src/commands/start.ts
================================================
import { iLog } from "@plasmo/utils/logging"

async function start() {
  iLog("Start the extension development...")
}

export default start


================================================
FILE: cli/plasmo/src/commands/version.ts
================================================
async function version() {
  console.log(process.env.APP_VERSION)
}

export default version


================================================
FILE: cli/plasmo/src/features/background-service-worker/bgsw-entry.ts
================================================
import { relative, resolve } from "path"
import { ensureDir, outputFile } from "fs-extra"

import { vLog } from "@plasmo/utils/logging"
import { toPosix } from "@plasmo/utils/path"

import { type PlasmoManifest } from "~features/manifest-factory/base"

export const createBgswEntry = async (
  { indexFilePath = "", withMessaging = false, withMainWorldScript = false },
  plasmoManifest: PlasmoManifest
) => {
  vLog("Creating BGSW entry")

  const bgswStaticDirectory = resolve(
    plasmoManifest.commonPath.staticDirectory,
    "background"
  )

  const bgswEntryFilePath = resolve(bgswStaticDirectory, "index.ts")
  const indexImportPath = relative(bgswStaticDirectory, indexFilePath)

  const bgswCode = [
    withMessaging && `import "./messaging"`,
    indexFilePath && `import "${toPosix(indexImportPath).slice(0, -3)}"`,
    withMainWorldScript && `import "./main-world-scripts"`
  ]
    .filter(Boolean)
    .join("\n")

  await ensureDir(bgswStaticDirectory)
  await outputFile(bgswEntryFilePath, bgswCode)
}


================================================
FILE: cli/plasmo/src/features/background-service-worker/bgsw-main-world-script.ts
================================================
import { relative, resolve } from "path"
import { camelCase } from "change-case"
import { outputFile } from "fs-extra"

import { vLog } from "@plasmo/utils/logging"
import { toPosix } from "@plasmo/utils/path"

import { type PlasmoManifest } from "~features/manifest-factory/base"

export const createBgswMainWorldInjector = async (
  plasmoManifest: PlasmoManifest
) => {
  try {
    const outputPath = resolve(
      plasmoManifest.commonPath.staticDirectory,
      "background",
      "main-world-scripts.ts"
    )

    const importStatements = plasmoManifest.mainWorldScriptList.map((s) => {
      const importMetadata = s.js.map((jsPath) => {
        const importName = camelCase(jsPath)
        const importPath = `url:${toPosix(
          relative(plasmoManifest.commonPath.entryManifestPath, jsPath)
        )}`

        return {
          importName,
          importPath
        }
      })

      const topImport = importMetadata
        .map((i) => `import ${i.importName} from "${i.importPath}"`)
        .join("\n")

      const importScript = importMetadata.map((i) => i.importName)

      const regCsScript = Object.entries(s).reduce(
        (out, [k, v]) => {
          if (k !== "js") {
            out[camelCase(k)] = v
          }
          return out
        },
        { id: importScript.join("-"), js: [] }
      )

      const regCsScriptCode = JSON.stringify(regCsScript).replace(
        /"js":\[\]/,
        `"js":[${importScript
          .map((imSrc) => `${imSrc}.split("/").pop().split("?")[0]`)
          .join(",")}]`
      )

      return [topImport, regCsScriptCode] as const
    })

    if (importStatements.length === 0) {
      return false
    }

    plasmoManifest.permissionSet.add("scripting")

    const code = `${importStatements.map(([top]) => top).join("\n")}
chrome.scripting.registerContentScripts([
  ${importStatements.map(([, reg]) => reg).join(",\n  ")}
]).catch(_ => {})
`

    await outputFile(outputPath, code)

    return true
  } catch (e) {
    vLog(e.message)
    return false
  }
}


================================================
FILE: cli/plasmo/src/features/background-service-worker/bgsw-messaging-declaration.ts
================================================
import { resolve } from "path"
import { outputFile } from "fs-extra"

import type { CommonPath } from "~features/extension-devtools/common-path"

export const MESSAGING_DECLARATION = `messaging` as const

const MESSAGING_DECLARATION_FILENAME = `${MESSAGING_DECLARATION}.d.ts`

export const outputMessagingDeclaration = (
  commonPath: CommonPath,
  declarationCode: string
) =>
  outputFile(
    resolve(commonPath.dotPlasmoDirectory, MESSAGING_DECLARATION_FILENAME),
    declarationCode
  )

export const createDeclarationCode = (messages: string[], ports: string[]) => `
import "@plasmohq/messaging"

interface MmMetadata {
\t${messages.join("\n\t")}
}

interface MpMetadata {
\t${ports.join("\n\t")}
}

declare module "@plasmohq/messaging" {
  interface MessagesMetadata extends MmMetadata {}
  interface PortsMetadata extends MpMetadata {}
}
`


================================================
FILE: cli/plasmo/src/features/background-service-worker/bgsw-messaging.ts
================================================
import { camelCase } from "change-case"
import glob from "fast-glob"
import { outputFile } from "fs-extra"
import { join, resolve } from "path"

import { isWriteable } from "@plasmo/utils/fs"
import { vLog, wLog } from "@plasmo/utils/logging"
import { toPosix } from "@plasmo/utils/path"

import {
  createDeclarationCode,
  outputMessagingDeclaration
} from "~features/background-service-worker/bgsw-messaging-declaration"
import { getRevHash } from "~features/helpers/crypto"
import { type PlasmoManifest } from "~features/manifest-factory/base"

const state = {
  md5Hash: ""
}

// TODO: cache these?
const createEntryCode = (
  importSection: string,
  messageSection: string,
  externalMessageSection: string,
  portSection: string
) => `// @ts-nocheck
globalThis.__plasmoInternalPortMap = new Map()

${importSection}

chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
  switch (request?.name) {
    ${externalMessageSection}
    default:
      break
  }

  return true
})

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  switch (request.name) {
    ${messageSection}
    default:
      break
  }

  return true
})

chrome.runtime.onConnect.addListener(function(port) {
  globalThis.__plasmoInternalPortMap.set(port.name, port)
  port.onMessage.addListener(function(request) {
    switch (port.name) {
      ${portSection}
      default:
        break
    }
  })
})

`

const getHandlerList = async (
  plasmoManifest: PlasmoManifest,
  dirName: "messages" | "messages/external" | "ports"
) => {
  const handlerDir = join(
    plasmoManifest.projectPath.backgroundDirectory,
    dirName
  )

  if (!(await isWriteable(handlerDir))) {
    return []
  }

  const handlerFileList = await glob("**/*.ts", {
    cwd: handlerDir,
    onlyFiles: true,
    ignore: dirName === "messages" ? ["external"] : []
  })

  return handlerFileList.map((filePath) => {
    const posixFilePath = toPosix(filePath)
    const handlerName = posixFilePath.slice(0, -3)
    const importPath = `${dirName}/${handlerName}`
    const importName = camelCase(importPath)

    return {
      importName,
      name: handlerName,
      declaration: `"${handlerName}" : {}`,
      importCode: `import { default as ${importName} } from "~background/${importPath}"`
    }
  })
}

const getMessageCode = (name: string, importName: string) => `case "${name}":
  ${importName}({
    ...request,
    sender
  }, {
    send: (p) => sendResponse(p)
  })
  break`

const getPortCode = (name: string, importName: string) => `case "${name}":
  ${importName}({
    port,
    ...request
  }, {
    send: (p) => port.postMessage(p)
  })
  break`

export const createBgswMessaging = async (plasmoManifest: PlasmoManifest) => {
  try {
    const handlerLists = await Promise.all([
      getHandlerList(plasmoManifest, "messages"),
      getHandlerList(plasmoManifest, "messages/external"),
      getHandlerList(plasmoManifest, "ports")
    ])

    const [messageHandlerList, externalMessageHandlerList, portHandlerList] =
      handlerLists

    vLog({ messageHandlerList, externalMessageHandlerList, portHandlerList })

    if (handlerLists.every((list) => list.length === 0)) {
      return false
    }

    // check if package.json has messaging API
    if (!("@plasmohq/messaging" in plasmoManifest.dependencies)) {
      wLog("@plasmohq/messaging is not installed, skipping messaging API")
      return false
    }

    const declarationCode = createDeclarationCode(
      messageHandlerList.map(({ declaration }) => declaration),
      portHandlerList.map(({ declaration }) => declaration)
    )

    const declarationMd5Hash = getRevHash(Buffer.from(declarationCode))

    if (state.md5Hash === declarationMd5Hash) {
      return true
    }

    state.md5Hash = declarationMd5Hash

    const entryCode = createEntryCode(
      [...messageHandlerList, ...externalMessageHandlerList, ...portHandlerList]
        .map((code) => code.importCode)
        .join("\n"),
      messageHandlerList
        .map((code) => getMessageCode(code.name, code.importName))
        .join("\n"),
      externalMessageHandlerList
        .map((code) => getMessageCode(code.name, code.importName))
        .join("\n"),
      portHandlerList
        .map((code) => getPortCode(code.name, code.importName))
        .join("\n")
    )

    await Promise.all([
      outputFile(
        resolve(
          plasmoManifest.commonPath.staticDirectory,
          "background",
          "messaging.ts"
        ),
        entryCode
      ),
      outputMessagingDeclaration(plasmoManifest.commonPath, declarationCode)
    ])

    return true
  } catch (e) {
    vLog(e.message)
    return false
  }
}


================================================
FILE: cli/plasmo/src/features/background-service-worker/update-bgsw-entry.ts
================================================
import { find } from "@plasmo/utils/array"
import { isAccessible } from "@plasmo/utils/fs"

import { createBgswEntry } from "~features/background-service-worker/bgsw-entry"
import { createBgswMainWorldInjector } from "~features/background-service-worker/bgsw-main-world-script"
import { createBgswMessaging } from "~features/background-service-worker/bgsw-messaging"
import { type PlasmoManifest } from "~features/manifest-factory/base"

export const updateBgswEntry = async (plasmoManifest: PlasmoManifest) => {
  const [bgswIndexFilePath, withMessaging, withMainWorldScript] =
    await Promise.all([
      find(plasmoManifest.projectPath.backgroundIndexList, isAccessible),
      createBgswMessaging(plasmoManifest),
      createBgswMainWorldInjector(plasmoManifest)
    ] as const)

  const hasBgsw =
    Boolean(bgswIndexFilePath) || withMessaging || withMainWorldScript

  if (hasBgsw) {
    await createBgswEntry(
      {
        indexFilePath: bgswIndexFilePath,
        withMessaging,
        withMainWorldScript
      },
      plasmoManifest
    )
  }

  return plasmoManifest.toggleBackground(hasBgsw)
}


================================================
FILE: cli/plasmo/src/features/env/env-config.ts
================================================
// Forked from https://github.com/vercel/next.js/blob/canary/packages/next-env/index.ts
import { readFile } from "fs/promises"
import { resolve } from "path"
import { constantCase } from "change-case"
import dotenv from "dotenv"
import { expand as dotenvExpand } from "dotenv-expand"

import { isFile, isReadable } from "@plasmo/utils/fs"
import { eLog, iLog, vLog } from "@plasmo/utils/logging"

import { getFlagMap } from "~features/helpers/flag"

export type Env = Record<string, string | undefined>
type LoadedEnvFiles = Array<{
  name: string
  contents: string
}>

export const INTERNAL_ENV_PREFIX = "PLASMO_"
export const PUBLIC_ENV_PREFIX = "PLASMO_PUBLIC_"

const envFileSet = new Set<string>()

export class PlasmoPublicEnv {
  data: Env

  constructor(_env: Env) {
    this.data = Object.keys(_env)
      .filter((k) => k.startsWith(PUBLIC_ENV_PREFIX))
      .reduce((env, key) => {
        env[key] = _env[key]
        return env
      }, {} as Env)
  }

  extends(rawData: Env) {
    iLog("Loaded environment variables from:", [...envFileSet])
    const clone = new PlasmoPublicEnv({ ...this.data })
    clone.data["NODE_ENV"] = process.env.NODE_ENV
    Object.entries(rawData).forEach(([key, value]) => {
      clone.data[`${INTERNAL_ENV_PREFIX}${constantCase(key)}`] = value
    })
    return clone
  }
}

function cascadeEnv(loadedEnvFiles: LoadedEnvFiles) {
  const parsed: dotenv.DotenvParseOutput = Object.assign({}, process.env)

  for (const { contents, name } of loadedEnvFiles) {
    try {
      envFileSet.add(name)
      const result = dotenvExpand({
        ignoreProcessEnv: true,
        parsed: dotenv.parse(contents)
      })

      if (!!result.parsed) {
        vLog(`Loaded env from ${name}`)
        const resultData = result.parsed || {}

        for (const [envKey, envValue] of Object.entries(resultData)) {
          if (typeof parsed[envKey] === "undefined") {
            try {
              parsed[envKey] = maybeParseJSON(envValue)
            } catch (ex) {
              eLog(`Failed to parse JSON directive ${envKey} in ${name}:`, ex.message)
            }

            // Pass through internal env variables
            if (envKey.startsWith(INTERNAL_ENV_PREFIX)) {
              process.env[envKey] = envValue
            }
          }
        }
      }
    } catch (err) {
      eLog(`Failed to load env from ${name}`, err)
    }
  }

  return parsed
}

const JSON_DIRECTIVE_RE = /^\s*json\((.+)\)\s*$/si

function maybeParseJSON(value: string): any {
  const match = value.match(JSON_DIRECTIVE_RE)
  return match ? JSON.parse(match[1]) : value
}

export const setInternalEnv = (env: Record<string, string>) => {
  for (const [key, value] of Object.entries(env)) {
    process.env[`${INTERNAL_ENV_PREFIX}${constantCase(key)}`] = value
  }
}

export const getEnvFileNames = () => {
  const nodeEnv = process.env.NODE_ENV
  const flagMap = getFlagMap()
  return [
    flagMap.envPath,

    `.env.${flagMap.browser}.local`,
    `.env.${flagMap.tag}.local`,
    `.env.${nodeEnv}.local`,

    // Don't include `.env.local` for `test` environment
    // since normally you expect tests to produce the same
    // results for everyone
    nodeEnv !== "test" ? `.env.local` : "",

    `.env.${flagMap.browser}`,
    `.env.${flagMap.tag}`,
    `.env.${nodeEnv}`,
    ".env"
  ].filter((s) => !!s)
}

export async function loadEnvConfig(dir: string) {
  const allDotEnvEntries = await Promise.all(
    getEnvFileNames()
      .map((envFile) => [envFile, resolve(dir, envFile)])
      .map(
        async ([envFile, filePath]) =>
          [
            envFile,
            filePath,
            (await isFile(filePath)) && (await isReadable(filePath))
          ] as const
      )
  )

  const envFiles: LoadedEnvFiles = await Promise.all(
    allDotEnvEntries
      .filter(([, , isValid]) => isValid)
      .map(async ([envFile, filePath]) => ({
        name: envFile,
        contents: await readFile(filePath, "utf8")
      }))
  )

  const combinedEnv = cascadeEnv(envFiles)

  const plasmoPublicEnv = new PlasmoPublicEnv(combinedEnv)

  return { combinedEnv, plasmoPublicEnv, loadedEnvFiles: envFiles }
}

export type EnvConfig = Awaited<ReturnType<typeof loadEnvConfig>>


================================================
FILE: cli/plasmo/src/features/env/env-declaration.ts
================================================
import { resolve } from "path"
import { outputFile } from "fs-extra"

import type { PlasmoManifest } from "~features/manifest-factory/base"

export const PROCESS_ENV_DECLARATION = `process.env` as const
const PROCESS_ENV_DECLARATION_FILENAME = `${PROCESS_ENV_DECLARATION}.d.ts`

const createDeclarationCode = (envKeys: string[]) => `
declare namespace NodeJS {
  interface ProcessEnv {
${envKeys.map((e) => `\t\t${e}?: string`).join("\n")}
  }
}
`

export async function outputEnvDeclaration({
  commonPath,
  publicEnv
}: PlasmoManifest) {
  const envKeys = Object.keys(publicEnv.data)

  if (envKeys.length === 0) {
    return
  }

  await outputFile(
    resolve(commonPath.dotPlasmoDirectory, PROCESS_ENV_DECLARATION_FILENAME),
    createDeclarationCode(envKeys)
  )
}


================================================
FILE: cli/plasmo/src/features/extension-devtools/common-path.ts
================================================
import { existsSync } from "fs"
import { basename, resolve } from "path"
import { cwd } from "process"

import { getFlagMap } from "~features/helpers/flag"

export const getCommonPath = (projectDirectory = cwd()) => {
  const flagMap = getFlagMap()

  process.env.PLASMO_PROJECT_DIR = projectDirectory

  const packageName = basename(projectDirectory)

  process.env.PLASMO_SRC_PATH = flagMap.srcPath

  const srcDirectory = resolve(projectDirectory, flagMap.srcPath)

  process.env.PLASMO_SRC_DIR = existsSync(srcDirectory)
    ? srcDirectory
    : projectDirectory

  process.env.PLASMO_BUILD_PATH = flagMap.buildPath

  const buildDirectory = resolve(projectDirectory, flagMap.buildPath)

  process.env.PLASMO_BUILD_DIR = buildDirectory

  const distDirectoryName = `${flagMap.target}-${flagMap.tag}`

  const distDirectory = resolve(buildDirectory, distDirectoryName)

  const dotPlasmoDirectory = resolve(projectDirectory, ".plasmo")

  const cacheDirectory = resolve(dotPlasmoDirectory, "cache")

  return {
    packageName,
    projectDirectory,

    buildDirectory,
    distDirectory,
    distDirectoryName,

    sourceDirectory: process.env.PLASMO_SRC_DIR,
    packageFilePath: resolve(projectDirectory, "package.json"),
    gitIgnorePath: resolve(projectDirectory, ".gitignore"),
    assetsDirectory: resolve(projectDirectory, "assets"),
    parcelConfig: resolve(projectDirectory, ".parcelrc"),

    dotPlasmoDirectory,
    cacheDirectory,

    plasmoVersionFilePath: resolve(cacheDirectory, "plasmo.version.json"),

    staticDirectory: resolve(dotPlasmoDirectory, "static"),
    genAssetsDirectory: resolve(dotPlasmoDirectory, "gen-assets"),
    entryManifestPath: resolve(
      dotPlasmoDirectory,
      `${flagMap.target}.plasmo.manifest.json`
    )
  }
}

export type CommonPath = ReturnType<typeof getCommonPath>


================================================
FILE: cli/plasmo/src/features/extension-devtools/content-script-config.ts
================================================
import { readFile } from "fs/promises"
import typescript, { type Node, type VariableDeclaration } from "typescript"

import type { ManifestContentScript } from "@plasmo/constants"
import { eLog, vLog } from "@plasmo/utils/logging"

import { parseAst } from "./parse-ast"

const {
  ScriptTarget,
  SyntaxKind,
  createSourceFile,
  isObjectLiteralExpression,
  isVariableStatement
} = typescript

export const extractContentScriptConfig = async (path: string) => {
  try {
    const sourceContent = await readFile(path, "utf8")
    if (sourceContent.length === 0) {
      return {
        isEmpty: true
      }
    }

    const sourceFile = createSourceFile(
      path,
      sourceContent,
      ScriptTarget.Latest,
      true
    )

    const variableDeclarationMap = sourceFile.statements
      .filter(isVariableStatement)
      .reduce(
        (output, node) => {
          node.declarationList.forEachChild((vd: VariableDeclaration) => {
            output[vd.name.getText()] = vd.initializer
          })

          return output
        },
        {} as Record<string, Node>
      )

    const configAST = variableDeclarationMap["config"]

    if (!configAST || !isObjectLiteralExpression(configAST)) {
      return null
    }

    const config = configAST.properties.reduce((output, node) => {
      if (node.getChildCount() < 3) {
        return output
      }

      const [keyNode, _, valueNode] = node.getChildren()

      const key = keyNode.getText()

      try {
        if (valueNode.kind === SyntaxKind.Identifier) {
          output[key] = parseAst(variableDeclarationMap[valueNode.getText()])
        } else {
          output[key] = parseAst(valueNode)
        }
      } catch (error) {
        eLog(error)
      }

      return output
    }, {} as ManifestContentScript)

    vLog("Parsed config:", config)

    return {
      config
    }
  } catch (error) {
    vLog(error)
    return null
  }
}


================================================
FILE: cli/plasmo/src/features/extension-devtools/generate-icons.ts
================================================
import { basename, resolve } from "path"
import { copy, ensureDir } from "fs-extra"
import sharp from "sharp"

import { find } from "@plasmo/utils/array"
import { isAccessible } from "@plasmo/utils/fs"
import { vLog, wLog } from "@plasmo/utils/logging"

import { getFlagMap } from "~features/helpers/flag"

import type { CommonPath } from "./common-path"

const getIconNameVariants = (size = 512 as string | number, name = "icon") => [
  `${name}${size}`,
  `${name}-${size}`,
  `${name}-${size}x${size}`
]

// Prefer icon -> medium -> large icon
const baseIconNames = [
  "icon",
  ...getIconNameVariants(),
  ...getIconNameVariants(1024)
]

/**
 * We pick icon in this order
 * 1. tag based icon
 * 2. env and tag based icon
 * 3. plain icon
 *
 * */
const getPrioritizedIconPaths = (iconNames = baseIconNames) => {
  const flagMap = getFlagMap()

  return iconNames
    .map((name) => [
      `${name}.${flagMap.tag}.${process.env.NODE_ENV}.png`,
      `${name}.${process.env.NODE_ENV}.png`,
      `${name}.${flagMap.tag}.png`,
      `${name}.png`
    ])
    .flat()
}

const iconSizeList = [128, 64, 48, 32, 16]

// Use this to cache the path resolving result
const iconState = {
  baseIconPaths: [] as string[],
  devProvidedIcons: {} as Record<string, string[]>
}

/**
 * Generate manifest icons from an icon in the assets directory
 * - One icon will be picked in the set { `icon`, `icon512`, `icon-512`, `icon-512x512`, `icon1024`, `icon-1024`, `icon-1024x1024` }
 * - Optionally, it will resolve `process.env.NODE_ENV` suffix, e.g: `icon.development.png`, `icon.production.png`
 * - The suffix is prioritized. Thus, if `icon512.development.png` exists, it will be picked over `icon512.png`
 * - Only png is supported
 */
export async function generateIcons({
  assetsDirectory,
  genAssetsDirectory
}: CommonPath) {
  // Precalculate the base icon paths
  if (iconState.baseIconPaths.length === 0) {
    const iconNameList = getPrioritizedIconPaths()
    iconState.baseIconPaths = iconNameList.map((name) =>
      resolve(assetsDirectory, name)
    )
  }

  const baseIconPath = await find(iconState.baseIconPaths, isAccessible)

  if (baseIconPath === undefined) {
    wLog("No icon found in assets directory")
    return
  }

  await ensureDir(genAssetsDirectory)

  const baseIcon = sharp(baseIconPath)
  const baseIconBuffer = await baseIcon.toBuffer()

  vLog(`${baseIconPath} found, creating resized icons`)

  await Promise.all(
    iconSizeList.map(async (width) => {
      if (iconState.devProvidedIcons[width] === undefined) {
        const devIconPath = getPrioritizedIconPaths(getIconNameVariants(width))
        iconState.devProvidedIcons[width] = devIconPath.map((name) =>
          resolve(assetsDirectory, name)
        )
      }

      const devProvidedIcon = await find(
        iconState.devProvidedIcons[width],
        isAccessible
      )

      const generatedIconPath = resolve(
        genAssetsDirectory,
        `icon${width}.plasmo.png`
      )

      if (process.env.NODE_ENV === "development") {
        if (devProvidedIcon !== undefined) {
          if (basename(devProvidedIcon).includes(".development.")) {
            return copy(devProvidedIcon, generatedIconPath)
          } else {
            return sharp(devProvidedIcon).grayscale().toFile(generatedIconPath)
          }
        } else {
          return sharp(Buffer.from(baseIconBuffer))
            .resize({ width, height: width })
            .greyscale(!basename(baseIconPath).includes(".development."))
            .toFile(generatedIconPath)
        }
      } else {
        return devProvidedIcon !== undefined
          ? copy(devProvidedIcon, generatedIconPath)
          : sharp(Buffer.from(baseIconBuffer))
              .resize({ width, height: width })
              .toFile(generatedIconPath)
      }
    })
  )
}


================================================
FILE: cli/plasmo/src/features/extension-devtools/get-bundle-config.ts
================================================
import { getFlagMap } from "~features/helpers/flag"

export const getBundleConfig = () => {
  const flagMap = getFlagMap()
  const { target, tag } = flagMap
  const [browser, manifestVersion] = target.split("-")

  // Potential runtime config here
  return {
    tag,
    target,
    browser,
    manifestVersion
  }
}

export type PlasmoBundleConfig = ReturnType<typeof getBundleConfig>


================================================
FILE: cli/plasmo/src/features/extension-devtools/git-ignore.ts
================================================
export const generateGitIgnore = () => `
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

#cache
.turbo
.next
.vercel

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*


# local env files
.env*

out/
build/
dist/

# plasmo - https://www.plasmo.com
.plasmo

# bpp - https://github.com/marketplace/actions/browser-platform-publisher
keys.json

# typescript
.tsbuildinfo
`


================================================
FILE: cli/plasmo/src/features/extension-devtools/package-file.ts
================================================
import { userInfo } from "os"
import { sentenceCase } from "change-case"
import getPackageJson, { type AbbreviatedVersion } from "package-json"

import type { ExtensionManifestV3 } from "@plasmo/constants"

import type { PackageManagerInfo } from "~features/helpers/package-manager"

const _generatePackage = async ({
  name = "plasmo-extension",
  version = "0.0.1",
  packageManager = {} as PackageManagerInfo
}) => {
  const baseData = {
    name,
    displayName: sentenceCase(name),
    version,
    description: "A basic Plasmo extension.",
    author: userInfo().username,

    packageManager: undefined as string | undefined,
    scripts: {
      dev: "plasmo dev",
      build: "plasmo build",
      package: "plasmo package"
    },
    dependencies: {
      plasmo: "workspace:*",
      react: "*",
      "react-dom": "*"
    } as Record<string, string>,
    devDependencies: {
      "@types/chrome": "*",
      "@types/node": "*",
      "@types/react": "*",
      "@types/react-dom": "*",
      prettier: "*",
      typescript: "*"
    } as Record<string, string>,
    manifest: {
      // permissions: [] as ValidManifestPermission[],
      host_permissions: ["https://*/*"]
    } as Partial<ExtensionManifestV3>
  }

  if (!packageManager || !packageManager.version) {
    delete baseData.packageManager
  } else {
    baseData.packageManager = `${packageManager.name}@${packageManager.version}`
  }

  return baseData
}

export type PackageJSON = Awaited<ReturnType<typeof _generatePackage>> & {
  homepage?: string
  contributors?: string[]
  peerDependencies?: Record<string, string>
}

type GenerateArgs = Parameters<typeof _generatePackage>[0]

export const generatePackage = async (p: GenerateArgs) =>
  (await _generatePackage(p)) as PackageJSON

export const resolveWorkspaceToLatestSemver = async (
  dependencies: Record<string, string>
) => {
  const output = {} as Record<string, string>

  await Promise.all(
    Object.entries(dependencies).map(async ([key, value]) => {
      if (key === "plasmo") {
        output[key] = process.env.APP_VERSION as string
      } else if (value === "workspace:*") {
        try {
          const remotePackageData = (await getPackageJson(key, {
            version: "latest"
          })) as unknown as AbbreviatedVersion
          output[key] = remotePackageData.version
        } catch {
          output[key] = value
        }
      } else {
        output[key] = value
      }
    })
  )

  return output
}


================================================
FILE: cli/plasmo/src/features/extension-devtools/parse-ast.ts
================================================
/**
 * Copyright (c) Plasmo Corp, foss@plasmo.com, MIT Licensed
 * ---
 * Adapted from https://github.com/dword-design/ts-ast-to-literal/blob/master/src/index.js
 * Copyright (c) Sebastian Landwehr info@sebastianlandwehr.com, MIT licensed
 */
import typescript, {
  type ArrayLiteralExpression,
  type Identifier,
  type LiteralExpression,
  type Node,
  type ObjectLiteralExpression,
  type PropertyAssignment
} from "typescript"

const { SyntaxKind } = typescript

export const parseAst = (node: Node) => {
  switch (node.kind) {
    case SyntaxKind.StringLiteral:
      return (node as LiteralExpression).text
    case SyntaxKind.TrueKeyword:
      return true
    case SyntaxKind.FalseKeyword:
      return false
    case SyntaxKind.NullKeyword:
      return null
    case SyntaxKind.NumericLiteral:
      return parseFloat((node as LiteralExpression).text)
    case SyntaxKind.ArrayLiteralExpression:
      return (node as ArrayLiteralExpression).elements
        .filter((node) => node.kind !== SyntaxKind.SpreadElement)
        .map(parseAst)
    case SyntaxKind.ObjectLiteralExpression:
      return (node as ObjectLiteralExpression).properties
        .filter(
          (property) =>
            property.kind === SyntaxKind.PropertyAssignment &&
            (property.name.kind === SyntaxKind.Identifier ||
              property.name.kind === SyntaxKind.StringLiteral)
        )
        .map((property: PropertyAssignment) => [
          (property.name as Identifier).escapedText ||
            (property.name as LiteralExpression).text,
          parseAst(property.initializer)
        ])
        .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
    default:
      return undefined
  }
}


================================================
FILE: cli/plasmo/src/features/extension-devtools/project-path.ts
================================================
import { resolve } from "path"

import { getEnvFileNames } from "~features/env/env-config"
import type { SupportedUiExt } from "~features/manifest-factory/ui-library"

import type { CommonPath } from "./common-path"

export enum WatchReason {
  None,

  EnvFile,

  PackageJson,
  AssetsDirectory,

  TabsDirectory,

  BackgroundIndex,
  BackgroundDirectory,

  ContentScriptIndex,
  ContentScriptsDirectory,

  NewtabIndex,
  NewtabHtml,

  SidePanelIndex,
  SidePanelHtml,

  DevtoolsIndex,
  DevtoolsHtml,

  PopupIndex,
  PopupHtml,

  OptionsIndex,
  OptionsHtml,

  SandboxIndex,
  SandboxesDirectory
}

type DirectoryWatchTuple = [WatchReason, string]

const getWatchReasonMap = (paths: string[], reason: WatchReason) =>
  paths.reduce(
    (output, path) => {
      output[path] = reason
      return output
    },
    {} as Record<string, WatchReason>
  )

export const getProjectPath = (
  { sourceDirectory, packageFilePath, assetsDirectory }: CommonPath,
  browserTarget: string,
  uiExts: SupportedUiExt[]
) => {
  /**
   * only pointing to 1 particular file path
   */
  const getModuleList = (moduleName: string) =>
    [".ts", ...uiExts, ".js"].flatMap((ext) => [
      resolve(sourceDirectory, `${moduleName}.${browserTarget}${ext}`),
      resolve(sourceDirectory, `${moduleName}.${process.env.NODE_ENV}${ext}`),
      resolve(sourceDirectory, `${moduleName}${ext}`)
    ])

  /**
   * crawl index, and only care about one extension
   */
  const getIndexList = (moduleName: string, exts = [".ts", ".js"]) =>
    exts.flatMap((ext) => [
      resolve(sourceDirectory, `${moduleName}.${browserTarget}${ext}`),
      resolve(sourceDirectory, moduleName, `index.${browserTarget}${ext}`),

      resolve(sourceDirectory, `${moduleName}.${process.env.NODE_ENV}${ext}`),
      resolve(
        sourceDirectory,
        moduleName,
        `index.${process.env.NODE_ENV}${ext}`
      ),

      resolve(sourceDirectory, `${moduleName}${ext}`),
      resolve(sourceDirectory, moduleName, `index${ext}`)
    ])

  const popupIndexList = getIndexList("popup", uiExts)
  const optionsIndexList = getIndexList("options", uiExts)
  const devtoolsIndexList = getIndexList("devtools", uiExts)
  const newtabIndexList = getIndexList("newtab", uiExts)
  const sidepanelIndexList = getIndexList("sidepanel", uiExts)

  const popupHtmlList = getIndexList("popup", [".html"])
  const optionsHtmlList = getIndexList("options", [".html"])
  const devtoolsHtmlList = getIndexList("devtools", [".html"])
  const newtabHtmlList = getIndexList("newtab", [".html"])
  const sidepanelHtmlList = getIndexList("sidepanel", [".html"])

  const envFileList = getEnvFileNames().map((f) => resolve(sourceDirectory, f))

  const backgroundIndexList = getIndexList("background")

  const contentIndexList = getModuleList("content")
  const sandboxIndexList = getModuleList("sandbox")

  const watchPathReasonMap = {
    [packageFilePath]: WatchReason.PackageJson,

    ...getWatchReasonMap(envFileList, WatchReason.EnvFile),

    ...getWatchReasonMap(contentIndexList, WatchReason.ContentScriptIndex),
    ...getWatchReasonMap(sandboxIndexList, WatchReason.SandboxIndex),

    ...getWatchReasonMap(backgroundIndexList, WatchReason.BackgroundIndex),

    ...getWatchReasonMap(popupIndexList, WatchReason.PopupIndex),
    ...getWatchReasonMap(optionsIndexList, WatchReason.OptionsIndex),
    ...getWatchReasonMap(devtoolsIndexList, WatchReason.DevtoolsIndex),
    ...getWatchReasonMap(newtabIndexList, WatchReason.NewtabIndex),
    ...getWatchReasonMap(sidepanelIndexList, WatchReason.SidePanelIndex),

    ...getWatchReasonMap(popupHtmlList, WatchReason.PopupHtml),
    ...getWatchReasonMap(optionsHtmlList, WatchReason.OptionsHtml),
    ...getWatchReasonMap(devtoolsHtmlList, WatchReason.DevtoolsHtml),
    ...getWatchReasonMap(newtabHtmlList, WatchReason.NewtabHtml),
    ...getWatchReasonMap(sidepanelHtmlList, WatchReason.SidePanelHtml)
  }

  const contentsDirectory = resolve(sourceDirectory, "contents")
  const sandboxesDirectory = resolve(sourceDirectory, "sandboxes")
  const tabsDirectory = resolve(sourceDirectory, "tabs")
  const backgroundDirectory = resolve(sourceDirectory, "background")
  const watchDirectoryEntries = [
    [WatchReason.SandboxesDirectory, sandboxesDirectory],
    [WatchReason.TabsDirectory, tabsDirectory],
    [WatchReason.ContentScriptsDirectory, contentsDirectory],
    [WatchReason.BackgroundDirectory, backgroundDirectory],
    [WatchReason.AssetsDirectory, assetsDirectory]
  ] as Array<DirectoryWatchTuple>

  const knownPathSet = new Set(Object.keys(watchPathReasonMap))

  const entryFileSet = new Set([
    ...backgroundIndexList,
    ...contentIndexList,
    ...sandboxIndexList,
    ...popupIndexList,
    ...optionsIndexList,
    ...devtoolsIndexList,
    ...newtabIndexList,
    ...sidepanelIndexList
  ])

  const isEntryPath = (path: string) => entryFileSet.has(path)

  return {
    popupIndexList,
    popupHtmlList,

    optionsIndexList,
    optionsHtmlList,

    devtoolsIndexList,
    devtoolsHtmlList,

    newtabIndexList,
    newtabHtmlList,

    backgroundIndexList,
    backgroundDirectory,

    contentIndexList,
    contentsDirectory,

    sidepanelIndexList,
    sidepanelHtmlList,

    sandboxIndexList,
    sandboxesDirectory,

    tabsDirectory,

    watchPathReasonMap,
    watchDirectoryEntries,

    isEntryPath,
    knownPathSet
  }
}

export type ProjectPath = ReturnType<typeof getProjectPath>


================================================
FILE: cli/plasmo/src/features/extension-devtools/project-watcher.ts
================================================
import { subscribe, type Event } from "@parcel/watcher"

import { PARCEL_WATCHER_BACKEND } from "@plasmo/constants/misc"
import { assertUnreachable } from "@plasmo/utils/assert"
import { hasFlag } from "@plasmo/utils/flags"
import { iLog, vLog, wLog } from "@plasmo/utils/logging"

import { updateBgswEntry } from "~features/background-service-worker/update-bgsw-entry"
import { type PlasmoManifest } from "~features/manifest-factory/base"

import { generateIcons } from "./generate-icons"
import { WatchReason } from "./project-path"

const ignore = ["node_modules", "build", ".plasmo", "coverage", ".git"]

export const createProjectWatcher = async (plasmoManifest: PlasmoManifest) => {
  if (hasFlag("--impulse")) {
    return null
  }

  const { default: isPathInside } = await import("is-path-inside")

  const { knownPathSet, watchPathReasonMap, watchDirectoryEntries } =
    plasmoManifest.projectPath

  vLog("Watching the following files:", knownPathSet)

  const getWatchReason = (path: string) => {
    // Quick track processing files already inside watch set
    if (knownPathSet.has(path)) {
      return watchPathReasonMap[path]
    }

    const validEntry = watchDirectoryEntries.find(([, dir]) =>
      isPathInside(path, dir)
    )

    if (!validEntry) {
      return WatchReason.None
    }

    // Add new path to the watch set if they are watch directories
    knownPathSet.add(path)
    return (watchPathReasonMap[path] = validEntry[0])
  }

  return subscribe(
    plasmoManifest.commonPath.projectDirectory,
    async (err, events) => {
      if (err) {
        throw err
      }

      await Promise.all(
        events.map(({ path, type }) =>
          handleProjectFile(type, path, getWatchReason(path), plasmoManifest)
        )
      )

      await plasmoManifest.write()
    },
    {
      backend: PARCEL_WATCHER_BACKEND,
      ignore
    }
  )
}

export const handleProjectFile = async (
  type: Event["type"],
  path: string,
  reason: WatchReason,
  plasmoManifest: PlasmoManifest
) => {
  const isEnabled = type !== "delete"

  switch (reason) {
    case WatchReason.None: {
      return
    }
    case WatchReason.EnvFile: {
      wLog("Environment file change detected, please restart the dev server.")
      await plasmoManifest.updateEnv()
      return
    }
    case WatchReason.AssetsDirectory: {
      iLog("Assets directory changed, update dynamic assets")
      await generateIcons(plasmoManifest.commonPath)
      return
    }
    case WatchReason.PackageJson: {
      iLog(
        "package.json changed, updating manifest overrides. You might need to restart the dev server."
      )
      await plasmoManifest.updatePackageData()
      return
    }
    case WatchReason.BackgroundDirectory:
    case WatchReason.BackgroundIndex: {
      await updateBgswEntry(plasmoManifest) // TODO: Make this a soft-check instead of a file-write ops
      return
    }
    case WatchReason.ContentScriptIndex:
    case WatchReason.ContentScriptsDirectory: {
      await plasmoManifest.toggleContentScript(path, isEnabled)

      if (plasmoManifest.hasMainWorldScript) {
        await updateBgswEntry(plasmoManifest)
      }
      return
    }

    case WatchReason.SandboxIndex:
    case WatchReason.SandboxesDirectory:
    case WatchReason.TabsDirectory: {
      await plasmoManifest.togglePage(path, isEnabled)
      return
    }

    case WatchReason.SidePanelIndex: {
      plasmoManifest.toggleSidePanel(isEnabled)
      return
    }

    case WatchReason.PopupIndex: {
      plasmoManifest.togglePopup(isEnabled)
      return
    }
    case WatchReason.OptionsIndex: {
      plasmoManifest.toggleOptions(isEnabled)
      return
    }
    case WatchReason.DevtoolsIndex: {
      plasmoManifest.toggleDevtools(isEnabled)
      return
    }
    case WatchReason.NewtabIndex: {
      plasmoManifest.toggleNewtab(isEnabled)
      return
    }

    case WatchReason.PopupHtml: {
      await plasmoManifest.scaffolder.createPageHtml("popup", isEnabled && path)
      return
    }
    case WatchReason.OptionsHtml: {
      await plasmoManifest.scaffolder.createPageHtml(
        "options",
        isEnabled && path
      )
      return
    }
    case WatchReason.DevtoolsHtml: {
      await plasmoManifest.scaffolder.createPageHtml(
        "devtools",
        isEnabled && path
      )
      return
    }
    case WatchReason.NewtabHtml: {
      await plasmoManifest.scaffolder.createPageHtml(
        "newtab",
        isEnabled && path
      )
      return
    }
    case WatchReason.SidePanelHtml: {
      await plasmoManifest.scaffolder.createPageHtml(
        "sidepanel",
        isEnabled && path
      )
      return
    }

    default:
      assertUnreachable(reason)
  }
}


================================================
FILE: cli/plasmo/src/features/extension-devtools/strip-underscore.ts
================================================
import { readdir, readFile, rename, stat, writeFile } from "fs/promises"
import { join, resolve } from "path"

const stripFileUnderscore = async (filePath: string) => {
  const fileContents = await readFile(resolve(filePath), "utf8")
  const newFileContents = fileContents.replace(/\/_/g, "/")
  await writeFile(filePath, newFileContents, "utf8")
}

export const stripUnderscore = async (dir = "") => {
  const entries = await readdir(dir)

  for (const entry of entries) {
    const entryPath = join(dir, entry)
    const entryStat = await stat(entryPath)
    if (entryStat.isDirectory()) {
      const newPath = entryPath.replace("/_", "/")
      await rename(entryPath, newPath)
      await stripUnderscore(newPath)
    } else {
      const newPath = entryPath.replace("/_", "/")
      await rename(entryPath, newPath)
      await stripFileUnderscore(newPath)
    }
  }
}


================================================
FILE: cli/plasmo/src/features/extension-devtools/template-path.ts
================================================
import { dirname, resolve } from "path"
import { fileURLToPath } from "url"

export const getTemplatePath = () => {
  const packagePath = dirname(fileURLToPath(import.meta.url))

  const templatePath = resolve(packagePath, "..", "templates")
  const staticTemplatePath = resolve(templatePath, "static")

  const initTemplatePackagePath = resolve(
    require.resolve("@plasmohq/init"),
    ".."
  )

  const initTemplatePath = resolve(initTemplatePackagePath, "templates")
  const initEntryPath = resolve(initTemplatePackagePath, "entries")
  const bppYaml = resolve(initTemplatePackagePath, "bpp.yml")

  return {
    templatePath,
    initTemplatePath,
    initEntryPath,
    staticTemplatePath,
    bppYaml
  }
}

export type TemplatePath = ReturnType<typeof getTemplatePath>


================================================
FILE: cli/plasmo/src/features/extension-devtools/tsconfig.ts
================================================
import { readFile } from "fs/promises"
import { resolve } from "path"
import { outputFile, outputJson } from "fs-extra"
import json5 from "json5"

import { MESSAGING_DECLARATION } from "~features/background-service-worker/bgsw-messaging-declaration"
import { PROCESS_ENV_DECLARATION } from "~features/env/env-declaration"
import type { CommonPath } from "~features/extension-devtools/common-path"

const DECLARATION_FILEPATH = `.plasmo/index.d.ts`

const INDEX_DECLARATION_CODE = [PROCESS_ENV_DECLARATION, MESSAGING_DECLARATION]
  .map((e) => `import "./${e}"`)
  .join("\n")

export const addDeclarationConfig = async (
  commonPath: CommonPath,
  filePath: string
) => {
  const tsconfigFilePath = resolve(commonPath.projectDirectory, "tsconfig.json")

  const tsconfigFile = await readFile(tsconfigFilePath, "utf8")
  const tsconfig = json5.parse(tsconfigFile)
  const includeSet = new Set(tsconfig.include)

  if (includeSet.has(filePath)) {
    return
  }

  tsconfig.include = [filePath, ...includeSet]

  await outputJson(tsconfigFilePath, tsconfig, {
    spaces: 2
  })
}

export const outputIndexDeclaration = async (commonPath: CommonPath) => {
  const declarationFilePath = resolve(
    commonPath.projectDirectory,
    DECLARATION_FILEPATH
  )

  await Promise.all([
    outputFile(declarationFilePath, INDEX_DECLARATION_CODE),
    addDeclarationConfig(commonPath, DECLARATION_FILEPATH)
  ])
}


================================================
FILE: cli/plasmo/src/features/extra/cache-busting.ts
================================================
import { lstat } from "fs/promises"
import { resolve } from "path"
import { emptyDir, ensureDir } from "fs-extra"

import { isAccessible } from "@plasmo/utils/fs"
import { vLog } from "@plasmo/utils/logging"

import type { CommonPath } from "~features/extension-devtools/common-path"

export async function cleanUpDotPlasmo({
  dotPlasmoDirectory,
  cacheDirectory
}: CommonPath) {
  await emptyDir(dotPlasmoDirectory)
  await ensureDir(cacheDirectory)
}

export async function cleanUpLargeCache(commonPath: CommonPath) {
  const parcelCacheDbFilePath = resolve(
    commonPath.cacheDirectory,
    "parcel",
    "data.mdb"
  )

  const hasCache = await isAccessible(parcelCacheDbFilePath)

  if (!hasCache) {
    return
  }

  const cacheDbFileSize = (await lstat(parcelCacheDbFilePath, { bigint: true }))
    .size

  const sizeInMB = cacheDbFileSize / 1024n ** 2n
  const sizeInGB = Number(sizeInMB) / 1024

  // TODO: calculate the limit based on some heuristic around the size of the project instead of a fixed value.
  const cacheLimitGB = 1.47

  if (sizeInGB > cacheLimitGB) {
    vLog(`Busting large build cache, size: ${sizeInGB.toFixed(2)} GB`)
    await cleanUpDotPlasmo(commonPath)
  }
}


================================================
FILE: cli/plasmo/src/features/extra/next-new-tab.ts
================================================
import { mkdir } from "fs/promises"
import { resolve } from "path"
import { sentenceCase } from "change-case"
import { copy, emptyDir, readJson, writeJson } from "fs-extra"

import { isAccessible } from "@plasmo/utils/fs"
import { sLog, vLog } from "@plasmo/utils/logging"

import { getCommonPath } from "~features/extension-devtools/common-path"
import type { PackageJSON } from "~features/extension-devtools/package-file"
import { stripUnderscore } from "~features/extension-devtools/strip-underscore"

export const generateNewTabManifest = (packageData: PackageJSON) => ({
  name: sentenceCase(packageData.name),
  description: packageData.description,
  version: packageData.version,
  manifest_version: 3,
  chrome_url_overrides: {
    newtab: "./index.html"
  }
})

export const nextNewTab = async () => {
  const { projectDirectory, packageFilePath } = getCommonPath()

  vLog("Creating a Plasmo + Nextjs based new tab extension")
  const out = resolve(projectDirectory, "out")

  const { default: chalk } = await import("chalk")

  if (!(await isAccessible(out))) {
    throw new Error(
      `${chalk.bold(
        "out"
      )} directory does not exist, did you forget to run "${chalk.underline(
        "next build && next export"
      )}"?`
    )
  }

  const packageData: PackageJSON = await readJson(packageFilePath)

  const extensionDirectory = resolve(projectDirectory, "extension")
  if (await isAccessible(extensionDirectory)) {
    const {
      default: { prompt }
    } = await import("inquirer")

    const { answer } = await prompt({
      type: "confirm",
      name: "answer",
      message: `${chalk.bold(
        "extension"
      )} directory already exists, do you want to overwrite it?`
    })

    if (!answer) {
      throw new Error("Aborted")
    }

    await emptyDir(extensionDirectory)
  }

  await mkdir(extensionDirectory)
  await copy(out, extensionDirectory)
  vLog("Extension created at:", extensionDirectory)

  await stripUnderscore(extensionDirectory)

  // Create manifest.json with chrome_url_overrides with index.html

  await writeJson(
    resolve(extensionDirectory, "manifest.json"),
    generateNewTabManifest(packageData),
    {
      spaces: 2
    }
  )

  sLog("Your extension is ready in the extension/ directory")
}


================================================
FILE: cli/plasmo/src/features/framework-update/version-tracker.ts
================================================
import { readJson, writeJson } from "fs-extra"
import getPackageJson, { type AbbreviatedVersion } from "package-json"
import semver from "semver"

import { isAccessible } from "@plasmo/utils/fs"
import { aLog, eLog, vLog, wLog } from "@plasmo/utils/logging"

import type { CommonPath } from "~features/extension-devtools/common-path"
import { cleanUpDotPlasmo } from "~features/extra/cache-busting"
import { getPackageManager } from "~features/helpers/package-manager"

export const updateVersionFile = async (commonPath: CommonPath) => {
  const { plasmoVersionFilePath } = commonPath

  if (!(await isAccessible(plasmoVersionFilePath))) {
    vLog("Plasmo version file not found, busting cache...")
    await cleanUpDotPlasmo(commonPath)
  } else {
    const cachedVersion = await readJson(plasmoVersionFilePath)
    const semverCachedVersion = semver.coerce(cachedVersion.version)
    const semverCurrentVersion = semver.coerce(process.env.APP_VERSION)!

    if (
      !semverCachedVersion ||
      semverCachedVersion.major < semverCurrentVersion.major ||
      (semverCachedVersion.major === semverCurrentVersion.major &&
        semverCachedVersion.minor < semverCurrentVersion.minor)
    ) {
      vLog("Plasmo updated, busting cache...")
      await cleanUpDotPlasmo(commonPath)
    }
  }

  await writeJson(plasmoVersionFilePath, { version: process.env.APP_VERSION })
}

export const checkNewVersion = async () => {
  // If the version is different, log a warning about new version is available
  const currentVersion = process.env.APP_VERSION

  // If the version is different, log a warning about new version is available
  try {
    // Get the latest version of plasmo
    const latestPackageJson = (await getPackageJson("plasmo", {
      version: "latest"
    })) as unknown as AbbreviatedVersion
    const latestVersion = latestPackageJson.version

    // If the version is different, log a warning about new version is available
    if (semver.lt(currentVersion, latestVersion)) {
      const { default: chalk } = await import("chalk")
      wLog(
        chalk.yellowBright(
          `A new version of plasmo is available: v${latestVersion}`
        )
      )
      const updateCmd = await getUpdateCmd(latestVersion)
      aLog(chalk.yellow(`Run ${updateCmd} to update`))
    }
  } catch (error) {
    eLog('Error fetching package information for "plasmo"', error)
  }
}

async function getUpdateCmd(version = "") {
  const packageManager = await getPackageManager()
  switch (packageManager.name) {
    case "npm":
      return `"npm i -S plasmo@${version}"`
    case "pnpm":
      return `"pnpm i plasmo@${version}"`
    case "yarn":
      return `"yarn add plasmo@${version}"`
  }
}


================================================
FILE: cli/plasmo/src/features/helpers/create-parcel-bundler.ts
================================================
import { dirname, join, resolve } from "path"
import ParcelFS from "@parcel/fs"
import ParcelPM from "@parcel/package-manager"
import { emptyDir, ensureDir, exists, readJson, writeJson } from "fs-extra"

import { getFlag, hasFlag } from "@plasmo/utils/flags"
import { wLog } from "@plasmo/utils/logging"

import { Parcel, type ParcelOptions } from "@plasmohq/parcel-core"

import type { PlasmoManifest } from "~features/manifest-factory/base"

import { getPackageManager } from "./package-manager"
import { setInternalEnv } from "~features/env/env-config"

const PackageInstallerMap = {
  npm: ParcelPM.Npm,
  yarn: ParcelPM.Yarn,
  pnpm: ParcelPM.Pnpm
}

export const createParcelBuilder = async (
  { commonPath, bundleConfig, publicEnv }: PlasmoManifest,
  { defaultTargetOptions = {}, ...options }: ParcelOptions
) => {
  const isProd = options.mode === "production"

  if (isProd) {
    await emptyDir(commonPath.distDirectory)
  } else {
    await ensureDir(commonPath.distDirectory)
  }

  process.env.__PLASMO_FRAMEWORK_INTERNAL_NO_MINIFY =
    isProd && hasFlag("--no-minify") ? "true" : "false"

  process.env.__PLASMO_FRAMEWORK_INTERNAL_SOURCE_MAPS = isProd
    ? hasFlag("--inline-source-maps")
      ? "inline"
      : hasFlag("--source-maps")
      ? "external"
      : "none"
    : hasFlag("--no-source-maps")
    ? "none"
    : "inline"

  process.env.__PLASMO_FRAMEWORK_INTERNAL_NO_CS_RELOAD = hasFlag(
    "--no-cs-reload"
  )
    ? "true"
    : "false"

  process.env.__PLASMO_FRAMEWORK_INTERNAL_ES_TARGET =
    (getFlag("--es-target") as any) || "es2022"

  const pmInfo = await getPackageManager()

  const inputFS = new ParcelFS.NodeFS()

  const PackageInstaller = PackageInstallerMap[pmInfo.name]

  const packageManager = new ParcelPM.NodePackageManager(
    inputFS,
    commonPath.projectDirectory,
    new PackageInstaller()
  )

  const baseConfig = require.resolve("@plasmohq/parcel-config")

  let runConfig = join(dirname(baseConfig), "run.json")

  const configJson = await readJson(baseConfig)

  if (hasFlag("--bundle-buddy")) {
    configJson.reporters = ["...", "@parcel/reporter-bundle-buddy"]
  }

  await writeJson(runConfig, configJson)

  if (await exists(commonPath.parcelConfig)) {
    runConfig = commonPath.parcelConfig

    if (isProd) {
      const customConfig = await readJson(runConfig)

      if (customConfig.extends !== "@plasmohq/parcel-config") {
        wLog(
          'The .parcelrc does not extend "@plasmohq/parcel-config", the result may be unexpected'
        )
      }
    }

    if (hasFlag("--bundle-buddy")) {
      wLog(
        'The "--bundle-buddy" flag does not work with a custom .parcelrc file'
      )
    }
  }

  const engines = {
    browsers:
      bundleConfig.manifestVersion === "mv2" &&
      bundleConfig.browser !== "firefox"
        ? ["IE 11"]
        : ["last 1 Chrome version"]
  }

  setInternalEnv(bundleConfig)

  const bundler = new Parcel({
    inputFS,
    packageManager,
    entries: commonPath.entryManifestPath,
    cacheDir: resolve(commonPath.cacheDirectory, "parcel"),
    config: runConfig,
    shouldAutoInstall: true,

    env: publicEnv.extends(bundleConfig).data,

    defaultTargetOptions: {
      ...defaultTargetOptions,
      engines,
      sourceMaps:
        process.env.__PLASMO_FRAMEWORK_INTERNAL_SOURCE_MAPS !== "none",
      distDir: commonPath.distDirectory
    },

    ...options
  })

  return bundler
}


================================================
FILE: cli/plasmo/src/features/helpers/crypto.ts
================================================
import { createHash } from "crypto"

/**
 * Fast hash for local file revving
 * DO NOT USE FOR SENSITIVE PURPOSES
 * md5 is good enough for file-revving: https://github.com/sindresorhus/rev-hash
 */
export const getRevHash = (buff: Buffer) =>
  createHash("md5").update(buff).digest("hex").slice(0, 18)


================================================
FILE: cli/plasmo/src/features/helpers/flag.ts
================================================
import { kebabCase } from "change-case"

import { getFlag } from "@plasmo/utils/flags"

export const getFlagMap = () => {
  const srcPath = getFlag("--src-path") || process.env.PLASMO_SRC_PATH || "src"

  const buildPath =
    getFlag("--build-path") || process.env.PLASMO_BUILD_PATH || "build"

  const tag =
    getFlag("--tag") ||
    process.env.PLASMO_TAG ||
    (process.env.NODE_ENV === "production" ? "prod" : "dev")

  const target = kebabCase(
    getFlag("--target") || process.env.PLASMO_TARGET || "chrome-mv3"
  )

  const [browser, manifestVersion] = target.split("-")

  const entry = getFlag("--entry") || "popup"

  const envPath = getFlag("--env")

  return {
    browser,
    manifestVersion,
    tag,
    srcPath,
    buildPath,
    target,
    entry,
    envPath
  }
}

const DEV_BUILD_COMMON_ARGS = `      --target=[string]           set the target (default: chrome-mv3)
      --tag=[string]              set the build tag (default: dev or prod depending on NODE_ENV)
      --src-path=[path]      
Download .txt
gitextract_fsxmfk6d/

├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 0.rfc.yml
│   │   ├── 1.bug.yml
│   │   ├── 2.example.yml
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── SECURITY.md
│   └── workflows/
│       ├── test-examples.yml
│       └── test-package.yml
├── .gitignore
├── .gitmodules
├── .npmrc
├── .prettierrc.mjs
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── LICENSE
├── api/
│   ├── messaging/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── jest.config.mjs
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── background.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   ├── message.ts
│   │   │   ├── port.ts
│   │   │   ├── pub-sub.ts
│   │   │   ├── relay.test.ts
│   │   │   ├── relay.ts
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   └── tsconfig.json
│   ├── persistent/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── background.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   └── selector/
│       ├── .gitignore
│       ├── LICENSE
│       ├── package.json
│       ├── src/
│       │   ├── background.ts
│       │   ├── hook.ts
│       │   ├── index.ts
│       │   ├── monitor.ts
│       │   └── types.ts
│       ├── tsconfig.json
│       └── tsup.config.ts
├── cli/
│   ├── create-plasmo/
│   │   ├── bin/
│   │   │   └── index.mjs
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── commands.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   └── plasmo/
│       ├── .eslintrc.js
│       ├── LICENSE
│       ├── README.md
│       ├── bin/
│       │   └── index.mjs
│       ├── i18n/
│       │   ├── README.de-DE.md
│       │   ├── README.fr-FR.md
│       │   ├── README.id-ID.md
│       │   ├── README.ja-JP.md
│       │   ├── README.ko-KR.md
│       │   ├── README.ru-RU.md
│       │   ├── README.tr-TR.md
│       │   ├── README.vi-VN.md
│       │   └── README.zh-CN.md
│       ├── index.mjs
│       ├── package.json
│       ├── src/
│       │   ├── commands/
│       │   │   ├── build.ts
│       │   │   ├── dev.ts
│       │   │   ├── help.ts
│       │   │   ├── index.ts
│       │   │   ├── init.ts
│       │   │   ├── package.ts
│       │   │   ├── start.ts
│       │   │   └── version.ts
│       │   ├── features/
│       │   │   ├── background-service-worker/
│       │   │   │   ├── bgsw-entry.ts
│       │   │   │   ├── bgsw-main-world-script.ts
│       │   │   │   ├── bgsw-messaging-declaration.ts
│       │   │   │   ├── bgsw-messaging.ts
│       │   │   │   └── update-bgsw-entry.ts
│       │   │   ├── env/
│       │   │   │   ├── env-config.ts
│       │   │   │   └── env-declaration.ts
│       │   │   ├── extension-devtools/
│       │   │   │   ├── common-path.ts
│       │   │   │   ├── content-script-config.ts
│       │   │   │   ├── generate-icons.ts
│       │   │   │   ├── get-bundle-config.ts
│       │   │   │   ├── git-ignore.ts
│       │   │   │   ├── package-file.ts
│       │   │   │   ├── parse-ast.ts
│       │   │   │   ├── project-path.ts
│       │   │   │   ├── project-watcher.ts
│       │   │   │   ├── strip-underscore.ts
│       │   │   │   ├── template-path.ts
│       │   │   │   └── tsconfig.ts
│       │   │   ├── extra/
│       │   │   │   ├── cache-busting.ts
│       │   │   │   └── next-new-tab.ts
│       │   │   ├── framework-update/
│       │   │   │   └── version-tracker.ts
│       │   │   ├── helpers/
│       │   │   │   ├── create-parcel-bundler.ts
│       │   │   │   ├── crypto.ts
│       │   │   │   ├── flag.ts
│       │   │   │   ├── loading-animation.ts
│       │   │   │   ├── package-manager.ts
│       │   │   │   ├── print.ts
│       │   │   │   ├── prompt.ts
│       │   │   │   └── traverse.ts
│       │   │   ├── manifest-factory/
│       │   │   │   ├── base.ts
│       │   │   │   ├── create-manifest.ts
│       │   │   │   ├── mv2.ts
│       │   │   │   ├── mv3.ts
│       │   │   │   ├── scaffolder.ts
│       │   │   │   ├── ui-library.ts
│       │   │   │   └── zip.ts
│       │   │   └── project-creator/
│       │   │       ├── from-existing-manifest.ts
│       │   │       ├── get-raw-name.ts
│       │   │       ├── git-init.ts
│       │   │       ├── index.ts
│       │   │       ├── install-dependencies.ts
│       │   │       └── print-ready.ts
│       │   ├── index.ts
│       │   └── type.ts
│       ├── templates/
│       │   ├── plasmo.d.ts
│       │   ├── static/
│       │   │   ├── background/
│       │   │   │   └── index.ts
│       │   │   ├── common/
│       │   │   │   ├── csui-container-react.tsx
│       │   │   │   ├── csui-container-vanilla.tsx
│       │   │   │   ├── csui.ts
│       │   │   │   ├── react.ts
│       │   │   │   └── vue.ts
│       │   │   ├── react17/
│       │   │   │   ├── content-script-ui-mount.tsx
│       │   │   │   ├── index.html
│       │   │   │   └── index.tsx
│       │   │   ├── react18/
│       │   │   │   ├── content-script-ui-mount.tsx
│       │   │   │   ├── index.html
│       │   │   │   └── index.tsx
│       │   │   ├── react19/
│       │   │   │   ├── content-script-ui-mount.tsx
│       │   │   │   ├── index.html
│       │   │   │   └── index.tsx
│       │   │   ├── svelte4/
│       │   │   │   ├── content-script-ui-mount.ts
│       │   │   │   ├── index.html
│       │   │   │   └── index.ts
│       │   │   ├── vanilla/
│       │   │   │   ├── index.html
│       │   │   │   └── index.ts
│       │   │   └── vue3/
│       │   │       ├── content-script-ui-mount.ts
│       │   │       ├── index.html
│       │   │       └── index.ts
│       │   └── tsconfig.base.json
│       └── tsconfig.json
├── core/
│   ├── parcel-bundler/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── bit-set.ts
│   │   │   ├── can-merge.ts
│   │   │   ├── create-bundle.ts
│   │   │   ├── create-ideal-graph.ts
│   │   │   ├── decorate-legacy-graph.ts
│   │   │   ├── get-entry-by-target.ts
│   │   │   ├── get-reachable-bundle-root.ts
│   │   │   ├── index.ts
│   │   │   ├── remove-bundle.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── parcel-compressor-utf8/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   └── utf8-transform.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-config/
│   │   ├── .gitignore
│   │   ├── index.json
│   │   └── package.json
│   ├── parcel-core/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── resolve-options.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-namer-manifest/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── parcel-optimizer-encapsulate/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-optimizer-es/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── blob-to-string.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-packager/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── get-web-accessible-resources.ts
│   │   │   ├── index.ts
│   │   │   └── utils.ts
│   │   └── tsconfig.json
│   ├── parcel-resolver/
│   │   ├── .gitignore
│   │   ├── index.mjs
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── dev-polyfills/
│   │   │   │   ├── react-refresh/
│   │   │   │   │   └── runtime.ts
│   │   │   │   └── react-refresh.ts
│   │   │   ├── handle-absolute-root.ts
│   │   │   ├── handle-alias.ts
│   │   │   ├── handle-plasmo-internal.ts
│   │   │   ├── handle-polyfill.ts
│   │   │   ├── handle-remote-caching.ts
│   │   │   ├── handle-tilde-src.ts
│   │   │   ├── index.ts
│   │   │   ├── polyfills/
│   │   │   │   ├── assert.ts
│   │   │   │   ├── buffer.ts
│   │   │   │   ├── console.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── crc-32/
│   │   │   │   │   └── crc32c.ts
│   │   │   │   ├── crc-32.ts
│   │   │   │   ├── crypto.ts
│   │   │   │   ├── domain.ts
│   │   │   │   ├── events.ts
│   │   │   │   ├── http.ts
│   │   │   │   ├── https.ts
│   │   │   │   ├── os.ts
│   │   │   │   ├── path.ts
│   │   │   │   ├── process.ts
│   │   │   │   ├── punycode.ts
│   │   │   │   ├── querystring.ts
│   │   │   │   ├── stream.ts
│   │   │   │   ├── string_decoder.ts
│   │   │   │   ├── sys.ts
│   │   │   │   ├── timers.ts
│   │   │   │   ├── tty.ts
│   │   │   │   ├── url.ts
│   │   │   │   ├── util.ts
│   │   │   │   ├── vm.ts
│   │   │   │   └── zlib.ts
│   │   │   └── shared.ts
│   │   └── tsconfig.json
│   ├── parcel-resolver-post/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── handle-hacks.ts
│   │   │   ├── handle-module-exports.ts
│   │   │   ├── handle-ts-path.ts
│   │   │   ├── index.ts
│   │   │   ├── shared.ts
│   │   │   └── utils.ts
│   │   └── tsconfig.json
│   ├── parcel-runtime/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── runtimes/
│   │   │   │   ├── background-service-runtime.ts
│   │   │   │   ├── page-runtime.ts
│   │   │   │   └── script-runtime.ts
│   │   │   ├── types.ts
│   │   │   └── utils/
│   │   │       ├── 0-patch-module.ts
│   │   │       ├── bgsw.ts
│   │   │       ├── hmr-check.ts
│   │   │       ├── hmr-utils.ts
│   │   │       ├── inject-socket.ts
│   │   │       ├── loading-indicator.ts
│   │   │       └── react-refresh.ts
│   │   ├── tsconfig.json
│   │   └── tsup.config.ts
│   ├── parcel-transformer-inject-env/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-inline-css/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── get-tagets.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-lab/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   └── state.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-manifest/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── runtime/
│   │   │   └── plasmo-default-background.ts
│   │   ├── src/
│   │   │   ├── csp-patch-hmr.ts
│   │   │   ├── handle-action.ts
│   │   │   ├── handle-background.ts
│   │   │   ├── handle-content-scripts.ts
│   │   │   ├── handle-declarative-net-request.ts
│   │   │   ├── handle-deep-loc.ts
│   │   │   ├── handle-dictionaries.ts
│   │   │   ├── handle-locales.ts
│   │   │   ├── handle-sandboxes.ts
│   │   │   ├── handle-tabs.ts
│   │   │   ├── index.ts
│   │   │   ├── normalize-manifest.ts
│   │   │   ├── schema.ts
│   │   │   ├── state.ts
│   │   │   ├── utils.ts
│   │   │   └── validate-version.ts
│   │   └── tsconfig.json
│   ├── parcel-transformer-svelte/
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── convert-error.ts
│   │   │   ├── convert-loc.ts
│   │   │   ├── index.ts
│   │   │   ├── source-map.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   └── parcel-transformer-vue/
│       ├── .gitignore
│       ├── package.json
│       ├── src/
│       │   └── index.ts
│       └── tsconfig.json
├── eslint.config.mjs
├── package.json
├── packages/
│   ├── framework-shared/
│   │   ├── build-socket/
│   │   │   ├── event.ts
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   └── init/
│       ├── .gitignore
│       ├── bpp.yml
│       ├── entries/
│       │   ├── background.ts
│       │   ├── content.ts
│       │   ├── contents/
│       │   │   ├── inline.tsx
│       │   │   └── overlay.tsx
│       │   ├── newtab.tsx
│       │   ├── options.tsx
│       │   └── popup.tsx
│       ├── index.json
│       ├── package.json
│       ├── templates/
│       │   ├── README.md
│       │   └── tsconfig.json
│       └── tsconfig.json
├── pnpm-workspace.yaml
├── renovate.json
├── scripts/
│   └── move-prettier-cjs-to-mjs.bash
└── turbo.json
Download .txt
SYMBOL INDEX (376 symbols across 123 files)

FILE: api/messaging/src/hook.ts
  function useMessageRelay (line 72) | function useMessageRelay<RequestBody = any>(

FILE: api/messaging/src/port.ts
  function reconnectHandler (line 27) | function reconnectHandler() {

FILE: api/messaging/src/pub-sub.ts
  type PubSubMessage (line 3) | type PubSubMessage = {

FILE: api/messaging/src/relay.test.ts
  class MessagePortMock (line 7) | class MessagePortMock {
    method clear (line 25) | clear() {

FILE: api/messaging/src/types.ts
  type MessagesMetadata (line 1) | interface MessagesMetadata {}
  type PortsMetadata (line 2) | interface PortsMetadata {}
  type MessageName (line 4) | type MessageName = keyof MessagesMetadata
  type PortName (line 5) | type PortName = keyof PortsMetadata
  type InternalSignal (line 7) | type InternalSignal = "__PLASMO_MESSAGING_PING__"
  type Request (line 10) | type Request<TName = any, TBody = any> = {
  type RelayMessage (line 25) | type RelayMessage<TName = any, TBody = any> = Request<TName, TBody> & {
  type InternalRequest (line 33) | type InternalRequest = {
  type Response (line 37) | type Response<TBody = any> = {
  type InternalHandler (line 41) | type InternalHandler = (request: InternalRequest) => void
  type Handler (line 43) | type Handler<
  type PortHandler (line 52) | type PortHandler<RequestBody = any, ResponseBody = any> = Handler<
  type MessageHandler (line 58) | type MessageHandler<RequestBody = any, ResponseBody = any> = Handler<
  type SendFx (line 64) | interface SendFx<TName = string> {
  type RelayFx (line 76) | interface RelayFx {
  type MessageRelayFx (line 91) | interface MessageRelayFx {
  type PortHook (line 95) | interface PortHook {
  type OriginContext (line 111) | type OriginContext =

FILE: api/selector/src/background.ts
  function selectorMessageHandler (line 6) | async function selectorMessageHandler(

FILE: api/selector/src/index.ts
  function sendInvalidSelectors (line 3) | async function sendInvalidSelectors(selectors: string[]) {

FILE: api/selector/src/types.ts
  type SelectorMessage (line 1) | type SelectorMessage = {

FILE: cli/create-plasmo/src/index.ts
  function main (line 12) | async function main() {

FILE: cli/plasmo/index.mjs
  function main (line 16) | async function main() {

FILE: cli/plasmo/src/commands/build.ts
  function build (line 13) | async function build() {

FILE: cli/plasmo/src/commands/dev.ts
  function dev (line 16) | async function dev() {

FILE: cli/plasmo/src/commands/help.ts
  function help (line 3) | async function help() {

FILE: cli/plasmo/src/commands/index.ts
  type ValidCommand (line 17) | type ValidCommand = keyof typeof runMap

FILE: cli/plasmo/src/commands/init.ts
  function init (line 18) | async function init() {

FILE: cli/plasmo/src/commands/package.ts
  function packageCmd (line 10) | async function packageCmd() {

FILE: cli/plasmo/src/commands/start.ts
  function start (line 3) | async function start() {

FILE: cli/plasmo/src/commands/version.ts
  function version (line 1) | async function version() {

FILE: cli/plasmo/src/features/background-service-worker/bgsw-messaging-declaration.ts
  constant MESSAGING_DECLARATION (line 6) | const MESSAGING_DECLARATION = `messaging` as const
  constant MESSAGING_DECLARATION_FILENAME (line 8) | const MESSAGING_DECLARATION_FILENAME = `${MESSAGING_DECLARATION}.d.ts`

FILE: cli/plasmo/src/features/env/env-config.ts
  type Env (line 13) | type Env = Record<string, string | undefined>
  type LoadedEnvFiles (line 14) | type LoadedEnvFiles = Array<{
  constant INTERNAL_ENV_PREFIX (line 19) | const INTERNAL_ENV_PREFIX = "PLASMO_"
  constant PUBLIC_ENV_PREFIX (line 20) | const PUBLIC_ENV_PREFIX = "PLASMO_PUBLIC_"
  class PlasmoPublicEnv (line 24) | class PlasmoPublicEnv {
    method constructor (line 27) | constructor(_env: Env) {
    method extends (line 36) | extends(rawData: Env) {
  function cascadeEnv (line 47) | function cascadeEnv(loadedEnvFiles: LoadedEnvFiles) {
  constant JSON_DIRECTIVE_RE (line 85) | const JSON_DIRECTIVE_RE = /^\s*json\((.+)\)\s*$/si
  function maybeParseJSON (line 87) | function maybeParseJSON(value: string): any {
  function loadEnvConfig (line 120) | async function loadEnvConfig(dir: string) {
  type EnvConfig (line 150) | type EnvConfig = Awaited<ReturnType<typeof loadEnvConfig>>

FILE: cli/plasmo/src/features/env/env-declaration.ts
  constant PROCESS_ENV_DECLARATION (line 6) | const PROCESS_ENV_DECLARATION = `process.env` as const
  constant PROCESS_ENV_DECLARATION_FILENAME (line 7) | const PROCESS_ENV_DECLARATION_FILENAME = `${PROCESS_ENV_DECLARATION}.d.ts`
  function outputEnvDeclaration (line 17) | async function outputEnvDeclaration({

FILE: cli/plasmo/src/features/extension-devtools/common-path.ts
  type CommonPath (line 64) | type CommonPath = ReturnType<typeof getCommonPath>

FILE: cli/plasmo/src/features/extension-devtools/generate-icons.ts
  function generateIcons (line 61) | async function generateIcons({

FILE: cli/plasmo/src/features/extension-devtools/get-bundle-config.ts
  type PlasmoBundleConfig (line 17) | type PlasmoBundleConfig = ReturnType<typeof getBundleConfig>

FILE: cli/plasmo/src/features/extension-devtools/package-file.ts
  type PackageJSON (line 55) | type PackageJSON = Awaited<ReturnType<typeof _generatePackage>> & {
  type GenerateArgs (line 61) | type GenerateArgs = Parameters<typeof _generatePackage>[0]

FILE: cli/plasmo/src/features/extension-devtools/project-path.ts
  type WatchReason (line 8) | enum WatchReason {
  type DirectoryWatchTuple (line 43) | type DirectoryWatchTuple = [WatchReason, string]
  type ProjectPath (line 192) | type ProjectPath = ReturnType<typeof getProjectPath>

FILE: cli/plasmo/src/features/extension-devtools/template-path.ts
  type TemplatePath (line 28) | type TemplatePath = ReturnType<typeof getTemplatePath>

FILE: cli/plasmo/src/features/extension-devtools/tsconfig.ts
  constant DECLARATION_FILEPATH (line 10) | const DECLARATION_FILEPATH = `.plasmo/index.d.ts`
  constant INDEX_DECLARATION_CODE (line 12) | const INDEX_DECLARATION_CODE = [PROCESS_ENV_DECLARATION, MESSAGING_DECLA...

FILE: cli/plasmo/src/features/extra/cache-busting.ts
  function cleanUpDotPlasmo (line 10) | async function cleanUpDotPlasmo({
  function cleanUpLargeCache (line 18) | async function cleanUpLargeCache(commonPath: CommonPath) {

FILE: cli/plasmo/src/features/framework-update/version-tracker.ts
  function getUpdateCmd (line 65) | async function getUpdateCmd(version = "") {

FILE: cli/plasmo/src/features/helpers/flag.ts
  constant DEV_BUILD_COMMON_ARGS (line 38) | const DEV_BUILD_COMMON_ARGS = `      --target=[string]           set the...

FILE: cli/plasmo/src/features/helpers/loading-animation.ts
  constant LOADING_TEXT (line 1) | const LOADING_TEXT = "🔄 Building"

FILE: cli/plasmo/src/features/helpers/package-manager.ts
  type PackageManager (line 7) | type PackageManager = "npm" | "pnpm" | "yarn"
  type PackageManagerInfo (line 9) | type PackageManagerInfo = {
  function getPMInfo (line 14) | async function getPMInfo(name: PackageManager): Promise<PackageManagerIn...
  function getPackageManager (line 20) | async function getPackageManager(): Promise<PackageManagerInfo> {

FILE: cli/plasmo/src/features/manifest-factory/base.ts
  method browser (line 83) | get browser() {
  method commonPath (line 88) | public get commonPath() {
  method projectPath (line 93) | public get projectPath() {
  method combinedEnv (line 101) | public get combinedEnv() {
  method publicEnv (line 106) | public get publicEnv() {
  method uiLibrary (line 119) | get uiLibrary() {
  method mountExt (line 124) | get mountExt() {
  method uiExts (line 129) | get uiExts() {
  method mainWorldScriptList (line 144) | get mainWorldScriptList() {
  method hasMainWorldScript (line 154) | get hasMainWorldScript() {
  method changed (line 168) | get changed() {
  method name (line 172) | get name() {
  method dependencies (line 177) | get dependencies() {
  method devDependencies (line 184) | get devDependencies() {
  method staticScaffoldPath (line 189) | get staticScaffoldPath() {
  method constructor (line 194) | protected constructor(public bundleConfig: PlasmoBundleConfig) {
  method initEnv (line 199) | private async initEnv(envRootDirectory = cwd()) {
  method startup (line 204) | async startup() {
  method postBuild (line 219) | async postBuild() {
  method updateEnv (line 242) | async updateEnv() {
  method updatePackageData (line 251) | async updatePackageData() {

FILE: cli/plasmo/src/features/manifest-factory/create-manifest.ts
  function createManifest (line 11) | async function createManifest(bundleConfig: PlasmoBundleConfig) {

FILE: cli/plasmo/src/features/manifest-factory/mv2.ts
  class PlasmoExtensionManifestMV2 (line 11) | class PlasmoExtensionManifestMV2 extends PlasmoManifest<ExtensionManifes...
    method constructor (line 12) | constructor(bundleConfig: PlasmoBundleConfig) {

FILE: cli/plasmo/src/features/manifest-factory/mv3.ts
  class PlasmoExtensionManifestMV3 (line 7) | class PlasmoExtensionManifestMV3 extends PlasmoManifest<ExtensionManifes...
    method constructor (line 8) | constructor(bundleConfig: PlasmoBundleConfig) {

FILE: cli/plasmo/src/features/manifest-factory/scaffolder.ts
  type ExtensionUIPage (line 15) | type ExtensionUIPage = "popup" | "options" | "devtools" | "newtab" | "si...
  class Scaffolder (line 17) | class Scaffolder {
    method projectPath (line 21) | get projectPath() {
    method commonPath (line 25) | get commonPath() {
    method mountExt (line 29) | get mountExt() {
    method constructor (line 33) | constructor(private plasmoManifest: PlasmoManifest) {}
    method init (line 35) | async init() {

FILE: cli/plasmo/src/features/manifest-factory/ui-library.ts
  type SupportedUiLibraryName (line 13) | type SupportedUiLibraryName = (typeof supportedUiLibraries)[number]
  type SupportedUiExt (line 17) | type SupportedUiExt = (typeof supportedUiExt)[number]
  type UiLibrary (line 24) | type UiLibrary = {
  type ScaffolderMountExt (line 32) | type ScaffolderMountExt = (typeof supportedMountExt)[number]
  type UiExtMap (line 34) | type UiExtMap = {

FILE: cli/plasmo/src/features/manifest-factory/zip.ts
  function toMB (line 10) | function toMB(bytes: number) {

FILE: cli/plasmo/src/features/project-creator/from-existing-manifest.ts
  function fromMv2 (line 113) | async function fromMv2(
  function fromMv3 (line 164) | async function fromMv3(

FILE: cli/plasmo/src/features/project-creator/git-init.ts
  function gitInit (line 39) | async function gitInit(

FILE: cli/plasmo/src/features/project-creator/index.ts
  class ProjectCreator (line 31) | class ProjectCreator {
    method constructor (line 34) | constructor(
    method create (line 40) | async create() {
    method createFrom (line 48) | async createFrom() {
    method createFromLocalTemplate (line 67) | async createFromLocalTemplate(absFromPath: string) {
    method createFromManifest (line 91) | async createFromManifest(absFromPath: string) {
    method createWith (line 110) | async createWith() {
    method createWithExample (line 119) | async createWithExample(exampleName: string) {
    method createBlank (line 164) | async createBlank() {
    method outputPackageData (line 169) | private async outputPackageData(
    method copyBlankInitFiles (line 212) | async copyBlankInitFiles() {
    method copyBppWorkflow (line 238) | async copyBppWorkflow() {

FILE: cli/plasmo/src/index.ts
  function defaultMode (line 12) | async function defaultMode() {
  function main (line 18) | async function main() {

FILE: cli/plasmo/src/type.ts
  type PlasmoCSConfig (line 7) | type PlasmoCSConfig = Omit<Partial<ManifestContentScript>, "js">
  type PlasmoContentScript (line 12) | type PlasmoContentScript = PlasmoCSConfig
  type Async (line 14) | type Async<T> = Promise<T> | T
  type Getter (line 16) | type Getter<T, P = any> = (props?: P) => Async<T>
  type InsertPosition (line 18) | type InsertPosition = "beforebegin" | "afterbegin" | "beforeend" | "afte...
  type ElementInsertOptions (line 20) | type ElementInsertOptions = {
  type ElementInsertOptionsList (line 25) | type ElementInsertOptionsList = ElementInsertOptions[]
  type GetElement (line 27) | type GetElement = Getter<Element>
  type GetElementInsertOptions (line 28) | type GetElementInsertOptions = Getter<ElementInsertOptions>
  type PlasmoCSUIOverlayAnchor (line 30) | type PlasmoCSUIOverlayAnchor = {
  type PlasmoCSUIInlineAnchor (line 36) | type PlasmoCSUIInlineAnchor = {
  type PlasmoCSUIAnchor (line 43) | type PlasmoCSUIAnchor = PlasmoCSUIOverlayAnchor | PlasmoCSUIInlineAnchor
  type PlasmoCSUIProps (line 45) | type PlasmoCSUIProps = {
  type PlasmoCSUIMountState (line 49) | type PlasmoCSUIMountState = {
  type PlasmoGetRootContainer (line 73) | type PlasmoGetRootContainer = (
  type PlasmoGetOverlayAnchor (line 79) | type PlasmoGetOverlayAnchor = GetElement
  type PlasmoGetOverlayAnchorList (line 80) | type PlasmoGetOverlayAnchorList = Getter<NodeList>
  type PlasmoGetInlineAnchor (line 82) | type PlasmoGetInlineAnchor = GetElement | GetElementInsertOptions
  type PlasmoGetInlineAnchorList (line 83) | type PlasmoGetInlineAnchorList = Getter<
  type PlasmoMountShadowHost (line 87) | type PlasmoMountShadowHost = (
  type PlasmoGetShadowHostId (line 94) | type PlasmoGetShadowHostId = Getter<string, PlasmoCSUIAnchor>
  type PlasmoGetStyle (line 96) | type PlasmoGetStyle = Getter<
  type PlasmoGetSfcStyleContent (line 101) | type PlasmoGetSfcStyleContent = Getter<string>
  type PlasmoWatchOverlayAnchor (line 106) | type PlasmoWatchOverlayAnchor = (
  type PlasmoCSUIContainerProps (line 110) | type PlasmoCSUIContainerProps = {
  type PlasmoCSUIJSXContainer (line 116) | type PlasmoCSUIJSXContainer = (
  type PlasmoCSUIHTMLContainer (line 119) | type PlasmoCSUIHTMLContainer = (
  type PlasmoCreateShadowRoot (line 123) | type PlasmoCreateShadowRoot = (
  type PlasmoRender (line 127) | type PlasmoRender<T> = (
  type PlasmoCSUIWatch (line 135) | type PlasmoCSUIWatch = (props: {
  type PlasmoCSUI (line 143) | type PlasmoCSUI<T> = {

FILE: cli/plasmo/templates/plasmo.d.ts
  type ProcessEnv (line 2) | interface ProcessEnv {

FILE: cli/plasmo/templates/static/common/csui.ts
  function createShadowDOM (line 3) | async function createShadowDOM<T>(Mount: PlasmoCSUI<T>) {
  type PlasmoCSUIShadowDOM (line 26) | type PlasmoCSUIShadowDOM = Awaited<ReturnType<typeof createShadowDOM>>
  function injectAnchor (line 28) | async function injectAnchor<T>(
  function createShadowContainer (line 62) | async function createShadowContainer<T>(
  function createAnchorObserver (line 117) | function createAnchorObserver<T>(Mount: PlasmoCSUI<T>) {

FILE: core/parcel-bundler/src/bit-set.ts
  constant BIGINT_ZERO (line 3) | const BIGINT_ZERO = 0n
  constant BIGINT_ONE (line 4) | const BIGINT_ONE = 1n
  class BitSet (line 9) | class BitSet<T> {
    method constructor (line 14) | constructor({
    method from (line 35) | static from<TT>(items: Array<TT>): BitSet<TT> {
    method union (line 44) | static union<TT>(a: BitSet<TT>, b: BitSet<TT>): BitSet<TT> {
    method getIndex (line 52) | private getIndex(item: T) {
    method add (line 56) | add(item: T) {
    method delete (line 60) | delete(item: T) {
    method has (line 64) | has(item: T): boolean {
    method intersect (line 68) | intersect(v: BitSet<T>) {
    method union (line 72) | union(v: BitSet<T>) {
    method clear (line 76) | clear() {
    method cloneEmpty (line 80) | cloneEmpty(): BitSet<T> {
    method clone (line 87) | clone(): BitSet<T> {
    method values (line 95) | values(): Array<T> {

FILE: core/parcel-bundler/src/can-merge.ts
  function canMerge (line 1) | function canMerge(a, b) {

FILE: core/parcel-bundler/src/create-bundle.ts
  function createBundle (line 6) | function createBundle(opts: {

FILE: core/parcel-bundler/src/create-ideal-graph.ts
  function createIdealGraph (line 20) | function createIdealGraph(

FILE: core/parcel-bundler/src/decorate-legacy-graph.ts
  function decorateLegacyGraph (line 12) | function decorateLegacyGraph(

FILE: core/parcel-bundler/src/get-entry-by-target.ts
  function getEntryByTarget (line 6) | function getEntryByTarget(

FILE: core/parcel-bundler/src/get-reachable-bundle-root.ts
  function getReachableBundleRoots (line 5) | function getReachableBundleRoots(asset, graph): Array<BundleRoot> {

FILE: core/parcel-bundler/src/index.ts
  constant EXTENSION_OPTIONS (line 17) | const EXTENSION_OPTIONS = {
  method loadConfig (line 39) | loadConfig({ options }) {
  method bundle (line 46) | bundle({ bundleGraph, config }) {
  method optimize (line 62) | optimize() {}

FILE: core/parcel-bundler/src/remove-bundle.ts
  function removeBundle (line 9) | function removeBundle(

FILE: core/parcel-bundler/src/types.ts
  type AssetId (line 13) | type AssetId = string
  type DependencyBundleGraph (line 15) | type DependencyBundleGraph = ContentGraph<
  type IdealGraph (line 29) | type IdealGraph = {
  type Bundle (line 36) | type Bundle = {
  type BundleRoot (line 52) | type BundleRoot = Asset
  type ResolvedBundlerConfig (line 60) | type ResolvedBundlerConfig = {

FILE: core/parcel-compressor-utf8/src/index.ts
  method compress (line 10) | async compress({ stream }) {

FILE: core/parcel-compressor-utf8/src/utf8-transform.ts
  class Utf8Transform (line 8) | class Utf8Transform extends Transform {
    method _transform (line 11) | _transform(chunk: Buffer, _: BufferEncoding, callback: TransformCallba...
    method _flush (line 15) | _flush(callback: TransformCallback) {
    method transformChunk (line 20) | private transformChunk(chunk: string, segmentSize: number = 1024): str...
    method transformSegment (line 30) | private transformSegment(segment: string): string {

FILE: core/parcel-core/src/index.ts
  constant INTERNAL_TRANSFORM (line 51) | const INTERNAL_TRANSFORM: symbol = Symbol("internal_transform")
  constant INTERNAL_RESOLVE (line 52) | const INTERNAL_RESOLVE: symbol = Symbol("internal_resolve")
  type SharedReference (line 54) | type SharedReference = number
  class Parcel (line 56) | class Parcel {
    method constructor (line 88) | constructor(options: InitialParcelOptions) {
    method _init (line 92) | async _init(): Promise<void> {
    method run (line 151) | async run(): Promise<BuildSuccessEvent> {
    method _end (line 167) | async _end(): Promise<void> {
    method _startNextBuild (line 176) | async _startNextBuild() {
    method watch (line 198) | async watch(
    method _build (line 252) | async _build({
    method _getWatcherSubscription (line 380) | async _getWatcherSubscription(): Promise<AsyncSubscription> {
    method startProfiling (line 418) | async startProfiling(): Promise<void> {
    method stopProfiling (line 428) | stopProfiling(): Promise<void> {
    method takeHeapSnapshot (line 438) | takeHeapSnapshot(): Promise<void> {
  class BuildError (line 444) | class BuildError extends ThrowableDiagnostic {
    method constructor (line 445) | constructor(diagnostic: Array<Diagnostic> | Diagnostic) {

FILE: core/parcel-core/src/resolve-options.ts
  constant LOCK_FILE_NAMES (line 22) | const LOCK_FILE_NAMES = ["yarn.lock", "package-lock.json", "pnpm-lock.ya...
  function generateInstanceId (line 25) | function generateInstanceId(entries: Array<FilePath>): string {
  function resolveOptions (line 31) | async function resolveOptions(initialOptions: InitialParcelOptions) {
  type ResolvedOptions (line 173) | type ResolvedOptions = Awaited<ReturnType<typeof resolveOptions>>
  function getRelativeConfigSpecifier (line 175) | function getRelativeConfigSpecifier(
  function determinePort (line 193) | function determinePort(

FILE: core/parcel-namer-manifest/src/index.ts
  method name (line 8) | name({ bundle }) {

FILE: core/parcel-optimizer-encapsulate/src/index.ts
  function getSourceMap (line 28) | function getSourceMap(options: PluginOptions, map: SourceMap) {
  method optimize (line 41) | async optimize({ bundle, contents, map, options }) {

FILE: core/parcel-optimizer-es/src/blob-to-string.ts
  type Blob (line 4) | type Blob = Buffer | Readable | string
  function bufferStream (line 6) | function bufferStream(stream: Readable): Promise<Buffer> {
  function blobToString (line 19) | async function blobToString(blob: Blob): Promise<string> {

FILE: core/parcel-optimizer-es/src/index.ts
  method optimize (line 19) | async optimize({

FILE: core/parcel-packager/src/get-web-accessible-resources.ts
  type Mv3Wars (line 17) | type Mv3Wars = ExtensionManifestV3["web_accessible_resources"]
  function appendMv2Wars (line 79) | function appendMv2Wars(
  function appendMv3Wars (line 93) | function appendMv3Wars(

FILE: core/parcel-packager/src/index.ts
  method package (line 23) | async package({ bundle, bundleGraph, options }) {

FILE: core/parcel-packager/src/utils.ts
  function getRelativePath (line 4) | function getRelativePath(from: NamedBundle, to: NamedBundle): string {

FILE: core/parcel-resolver-post/src/handle-hacks.ts
  function handleHacks (line 6) | async function handleHacks({

FILE: core/parcel-resolver-post/src/handle-module-exports.ts
  function handleModuleExport (line 4) | async function handleModuleExport({

FILE: core/parcel-resolver-post/src/handle-ts-path.ts
  type TsPaths (line 41) | type TsPaths = string[]
  type TsPathsMap (line 43) | type TsPathsMap = Map<string, TsPaths>
  function handleTsPath (line 50) | async function handleTsPath(
  function loadTsPathsMap (line 96) | function loadTsPathsMap(tsConfigs: TSConfig[]) {
  function loadPathsFromTSConfig (line 113) | function loadPathsFromTSConfig(
  function attemptResolve (line 133) | function attemptResolve({ specifier, dependency }: ResolverProps) {
  function attemptResolveArray (line 162) | function attemptResolveArray(
  type TSConfig (line 195) | type TSConfig = { compilerOptions: CompilerOptions; filePath: string }
  function getTsconfigCompilerOptions (line 197) | async function getTsconfigCompilerOptions(

FILE: core/parcel-resolver-post/src/index.ts
  method resolve (line 8) | async resolve(props) {

FILE: core/parcel-resolver-post/src/shared.ts
  type ResolveFx (line 17) | type ResolveFx = ConstructorParameters<typeof Resolver>[0]["resolve"]
  type ResolverResult (line 19) | type ResolverResult = ResolveResult
  type ResolverProps (line 20) | type ResolverProps = Parameters<ResolveFx>[0]

FILE: core/parcel-resolver-post/src/utils.ts
  constant WEBPACK_IMPORT_REGEX (line 7) | const WEBPACK_IMPORT_REGEX = /\S+-loader\S*!\S+/g
  function checkWebpackSpecificImportSyntax (line 9) | function checkWebpackSpecificImportSyntax(specifier = "") {
  function trimStar (line 19) | function trimStar(str: string) {
  function trim (line 23) | function trim(str: string, trim: string) {
  function findModule (line 38) | function findModule(

FILE: core/parcel-resolver/index.mjs
  function buildProdPolyfills (line 14) | async function buildProdPolyfills() {
  function buildDevPolyfills (line 29) | async function buildDevPolyfills() {
  function main (line 43) | async function main() {

FILE: core/parcel-resolver/src/handle-absolute-root.ts
  function handleAbsoluteRoot (line 11) | async function handleAbsoluteRoot({

FILE: core/parcel-resolver/src/handle-alias.ts
  function handleAlias (line 7) | async function handleAlias({

FILE: core/parcel-resolver/src/handle-plasmo-internal.ts
  function handlePlasmoInternal (line 20) | async function handlePlasmoInternal({

FILE: core/parcel-resolver/src/handle-polyfill.ts
  function handlePolyfill (line 3) | async function handlePolyfill({

FILE: core/parcel-resolver/src/handle-remote-caching.ts
  function handleRemoteCaching (line 23) | async function handleRemoteCaching({

FILE: core/parcel-resolver/src/handle-tilde-src.ts
  function handleTildeSrc (line 12) | async function handleTildeSrc({

FILE: core/parcel-resolver/src/index.ts
  method resolve (line 12) | async resolve(props) {

FILE: core/parcel-resolver/src/shared.ts
  type ResolveFx (line 31) | type ResolveFx = ConstructorParameters<typeof Resolver>[0]["resolve"]
  type ResolverResult (line 33) | type ResolverResult = ResolveResult
  type ResolverProps (line 35) | type ResolverProps = Parameters<ResolveFx>[0]

FILE: core/parcel-runtime/src/index.ts
  method loadConfig (line 25) | async loadConfig({ config }) {
  method apply (line 48) | apply({ bundle, options, config, bundleGraph }) {

FILE: core/parcel-runtime/src/runtimes/background-service-runtime.ts
  function consolidateUpdate (line 32) | async function consolidateUpdate(forced = false) {

FILE: core/parcel-runtime/src/runtimes/page-runtime.ts
  constant PORT_NAME (line 20) | const PORT_NAME = `${PAGE_PORT_PREFIX}${module.id}__`

FILE: core/parcel-runtime/src/runtimes/script-runtime.ts
  constant PORT_NAME (line 22) | const PORT_NAME = `${SCRIPT_PORT_PREFIX}${module.id}__`
  function consolidateUpdate (line 28) | async function consolidateUpdate() {
  function reloadPort (line 39) | function reloadPort() {
  function setupPort (line 63) | function setupPort() {

FILE: core/parcel-runtime/src/types.ts
  type PlasmoRuntime (line 7) | type PlasmoRuntime = (typeof plasmoRuntimeList)[number]
  type Window (line 11) | interface Window {
  type NodeModule (line 16) | interface NodeModule {
  type ExtensionApi (line 21) | type ExtensionApi = typeof globalThis.chrome
  type ParcelModule (line 23) | interface ParcelModule {
  type ParcelBundle (line 32) | interface ParcelBundle {
  type ParcelAsset (line 47) | type ParcelAsset = [ParcelBundle, string]
  type RuntimeData (line 49) | type RuntimeData = {
  type HmrAsset (line 69) | type HmrAsset = {
  type HmrMessage (line 79) | type HmrMessage =
  type BackgroundMessage (line 94) | type BackgroundMessage = {

FILE: core/parcel-runtime/src/utils/0-patch-module.ts
  function Module (line 19) | function Module(moduleName: string) {
  function triggerReload (line 41) | async function triggerReload(fullReload = false) {
  function getHostname (line 52) | function getHostname() {
  function getSocketHostname (line 62) | function getSocketHostname() {
  function getPort (line 70) | function getPort() {
  constant PAGE_PORT_PREFIX (line 74) | const PAGE_PORT_PREFIX = `__plasmo_runtime_page_`
  constant SCRIPT_PORT_PREFIX (line 75) | const SCRIPT_PORT_PREFIX = `__plasmo_runtime_script_`

FILE: core/parcel-runtime/src/utils/bgsw.ts
  function pollingDevServer (line 9) | async function pollingDevServer(delay = 1470) {

FILE: core/parcel-runtime/src/utils/hmr-check.ts
  function getParents (line 15) | function getParents(
  function hmrAcceptCheck (line 44) | function hmrAcceptCheck(
  function hmrAcceptCheckOne (line 79) | function hmrAcceptCheckOne(
  function isDependencyOfBundle (line 117) | function isDependencyOfBundle(bundle: ParcelBundle, id: string) {

FILE: core/parcel-runtime/src/utils/hmr-utils.ts
  function hmrDownload (line 5) | function hmrDownload(asset: HmrAsset) {
  function hmrApplyUpdates (line 28) | async function hmrApplyUpdates(assets: Array<HmrAsset>) {
  function updateLink (line 63) | function updateLink(link: Element) {
  function reloadCSS (line 80) | function reloadCSS() {
  function hmrApply (line 112) | function hmrApply(bundle: ParcelBundle, asset: HmrAsset) {
  function hmrDelete (line 148) | function hmrDelete(bundle: ParcelBundle, id: string) {
  function hmrDispose (line 178) | function hmrDispose(bundle: ParcelBundle, id: string) {
  function hmrAccept (line 194) | function hmrAccept(bundle: ParcelBundle, id: string) {

FILE: core/parcel-runtime/src/utils/inject-socket.ts
  function getBaseSocketUri (line 7) | function getBaseSocketUri(port = getPort()) {
  function wsErrorHandler (line 19) | function wsErrorHandler(e: ErrorEvent) {
  function injectBuilderSocket (line 25) | function injectBuilderSocket(
  function injectHmrSocket (line 44) | function injectHmrSocket(

FILE: core/parcel-runtime/src/utils/loading-indicator.ts
  constant LOADING_ID (line 11) | const LOADING_ID = "__plasmo-loading__"
  function createTrustedPolicy (line 13) | function createTrustedPolicy() {
  function getLoader (line 40) | function getLoader() {
  function isLoaderUnavailable (line 44) | function isLoaderUnavailable() {
  function createLoader (line 48) | function createLoader() {
  function injectLoaderEl (line 130) | function injectLoaderEl(loaderEl: HTMLElement) {

FILE: core/parcel-runtime/src/utils/react-refresh.ts
  function injectReactRefresh (line 3) | async function injectReactRefresh() {

FILE: core/parcel-transformer-inject-env/src/index.ts
  method transform (line 6) | async transform({ asset, options }) {

FILE: core/parcel-transformer-inline-css/src/get-tagets.ts
  function getTargets (line 14) | function getTargets(browsers) {

FILE: core/parcel-transformer-inline-css/src/index.ts
  method transform (line 17) | async transform({ asset, options }) {

FILE: core/parcel-transformer-lab/src/index.ts
  function collectDependencies (line 11) | async function collectDependencies() {}
  method transform (line 14) | async transform({ asset, options }) {

FILE: core/parcel-transformer-manifest/src/csp-patch-hmr.ts
  constant DEFAULT_INSERT (line 3) | const DEFAULT_INSERT = "'unsafe-eval'"
  function cspPatchHMR (line 5) | function cspPatchHMR(

FILE: core/parcel-transformer-manifest/src/handle-action.ts
  function handleAction (line 5) | async function handleAction() {

FILE: core/parcel-transformer-manifest/src/handle-background.ts
  function handleMV2Background (line 21) | function handleMV2Background(program: MV2Data) {
  function handleMV3Background (line 26) | function handleMV3Background(program: MV3Data) {
  function handleFirefoxMV3Background (line 42) | function handleFirefoxMV3Background(program: MV3Data) {
  function handleMV2BackgroundScript (line 54) | function handleMV2BackgroundScript(program: MV2Data) {
  function handleMV3BackgroundServiceWorker (line 80) | function handleMV3BackgroundServiceWorker(program: MV3Data) {
  function handleMV2HotCsp (line 120) | function handleMV2HotCsp(program: MV2Data) {
  function handleMV3HotCsp (line 132) | function handleMV3HotCsp(program: MV3Data) {

FILE: core/parcel-transformer-manifest/src/handle-content-scripts.ts
  function handleContentScripts (line 5) | function handleContentScripts() {

FILE: core/parcel-transformer-manifest/src/handle-declarative-net-request.ts
  function handleDeclarativeNetRequest (line 5) | async function handleDeclarativeNetRequest() {

FILE: core/parcel-transformer-manifest/src/handle-deep-loc.ts
  constant DEEP_LOCS (line 8) | const DEEP_LOCS = [

FILE: core/parcel-transformer-manifest/src/handle-dictionaries.ts
  function handleDictionaries (line 6) | function handleDictionaries() {

FILE: core/parcel-transformer-manifest/src/handle-locales.ts
  function handleLocales (line 8) | async function handleLocales() {

FILE: core/parcel-transformer-manifest/src/handle-sandboxes.ts
  function handleSandboxes (line 7) | async function handleSandboxes() {

FILE: core/parcel-transformer-manifest/src/handle-tabs.ts
  function handleTabs (line 7) | async function handleTabs() {

FILE: core/parcel-transformer-manifest/src/index.ts
  function collectDependencies (line 28) | async function collectDependencies() {
  method transform (line 66) | async transform({ asset, options }) {

FILE: core/parcel-transformer-manifest/src/schema.ts
  type MV3Data (line 504) | type MV3Data = FromSchema<typeof MV3Schema>
  type MV2Data (line 549) | type MV2Data = FromSchema<typeof MV2Schema>
  type ManifestData (line 551) | type ManifestData = MV2Data | MV3Data

FILE: core/parcel-transformer-manifest/src/state.ts
  type ExtraAsset (line 11) | type ExtraAsset = TransformerResult
  type StateParams (line 45) | type StateParams = Parameters<typeof storeState>
  type State (line 47) | type State = Partial<Awaited<ReturnType<typeof storeState>>>

FILE: core/parcel-transformer-manifest/src/validate-version.ts
  constant MIN_VERSION (line 1) | const MIN_VERSION = 0
  constant MAX_VERSION (line 2) | const MAX_VERSION = 65535

FILE: core/parcel-transformer-svelte/src/convert-error.ts
  function convertError (line 16) | function convertError(

FILE: core/parcel-transformer-svelte/src/convert-loc.ts
  function convertLOC (line 15) | function convertLOC(

FILE: core/parcel-transformer-svelte/src/index.ts
  method loadConfig (line 18) | async loadConfig({ config, options }) {
  method transform (line 52) | async transform({ asset, config, options, logger }) {

FILE: core/parcel-transformer-svelte/src/source-map.ts
  function mapSourceMapPath (line 14) | function mapSourceMapPath(mapSourceRoot: string, sourcePath: string) {
  function extendSourceMap (line 25) | function extendSourceMap(

FILE: core/parcel-transformer-svelte/src/types.ts
  type Transform (line 7) | type Transform = ConstructorParameters<typeof Transformer>[0]["transform"]
  type MutableAsset (line 9) | type MutableAsset = Parameters<Transform>[0]["asset"]
  type Options (line 10) | type Options = Parameters<Transform>[0]["options"]

FILE: core/parcel-transformer-vue/src/index.ts
  constant MODULE_BY_NAME_RE (line 24) | const MODULE_BY_NAME_RE = /\.module\./
  method loadConfig (line 28) | async loadConfig({ config }) {
  method canReuseAST (line 53) | canReuseAST({ ast }) {
  method parse (line 56) | async parse({ asset, options }) {
  method transform (line 93) | async transform({ asset, options, resolve, config }) {
  function createDiagnostic (line 170) | function createDiagnostic(err, filePath) {
  function processPipeline (line 206) | async function processPipeline({
  function createMap (line 510) | function createMap(rawMap, projectRoot: string) {

FILE: packages/framework-shared/build-socket/event.ts
  type BuildSocketEvent (line 1) | enum BuildSocketEvent {

FILE: packages/init/entries/newtab.tsx
  function IndexNewtab (line 3) | function IndexNewtab() {

FILE: packages/init/entries/options.tsx
  function IndexOptions (line 3) | function IndexOptions() {

FILE: packages/init/entries/popup.tsx
  function IndexPopup (line 3) | function IndexPopup() {
Condensed preview — 334 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (570K chars).
[
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 3298,
    "preview": "## Code of Conduct\n\n### Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our communi"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 4591,
    "preview": "# Contributing to Plasmo\n\nTo contribute to [our examples](https://github.com/PlasmoHQ/examples/), please see **[Adding e"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 755,
    "preview": "# These are supported funding model platforms\n\ngithub: PlasmoHQ\n\n# patreon: # Replace with a single Patreon username\n# o"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/0.rfc.yml",
    "chars": 2506,
    "preview": "name: ⚡ Request for Comments\ndescription: File an RFC for Feature Request/Enhancement/Refactor\ntitle: \"[RFC] \"\nlabels: ["
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1.bug.yml",
    "chars": 2744,
    "preview": "name: 🐛 Bug Report\ndescription: File a bug report\ntitle: \"[BUG] \"\nlabels: [\"bug\", \"triage\"]\nbody:\n  - type: markdown\n   "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2.example.yml",
    "chars": 1210,
    "preview": "name: 📓 Request/Improve an Example\ndescription: Request or Improve a Plasmo Framework with-* example\ntitle: \"[EXP] \"\nlab"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 179,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Join our Discord server\n    url: https://www.plasmo.com/s/d\n    abo"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 713,
    "preview": "<!--\nThanks for opening a PR! Your contribution is much appreciated.\nIn order to make sure your PR is handled as smoothl"
  },
  {
    "path": ".github/SECURITY.md",
    "chars": 125,
    "preview": "Contact: security@plasmo.com\nExpires: 2100-01-01T00:00:00.000Z\nAcknowledgments: https://www.plasmo.com/security/hall-of-"
  },
  {
    "path": ".github/workflows/test-examples.yml",
    "chars": 1342,
    "preview": "name: Test examples\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  build:\n"
  },
  {
    "path": ".github/workflows/test-package.yml",
    "chars": 1287,
    "preview": "name: Test package manager execution\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n    inputs:\n      tag:\n "
  },
  {
    "path": ".gitignore",
    "chars": 383,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\nnode_modules\n.pnp\n"
  },
  {
    "path": ".gitmodules",
    "chars": 1152,
    "preview": "[submodule \"packages/rps\"]\n\tpath = packages/rps\n\turl = https://github.com/PlasmoHQ/rps.git\n\tbranch = main\n[submodule \"pa"
  },
  {
    "path": ".npmrc",
    "chars": 190,
    "preview": "save-workspace-protocol = true\nprefer-workspace-packages = true\nsave-exact = true\nlink-workspace-packages = true\nstrict-"
  },
  {
    "path": ".prettierrc.mjs",
    "chars": 681,
    "preview": "/**\n * @type {import('prettier').Options}\n */\nexport default {\n  printWidth: 80,\n  tabWidth: 2,\n  useTabs: false,\n  semi"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 52,
    "preview": "{\n  \"recommendations\": [\"esbenp.prettier-vscode\"]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 384,
    "preview": "{\n  \"editor.formatOnSave\": true,\n  \"files.exclude\": {\n    \"**/.git\": true,\n    \"**/.svn\": true,\n    \"**/.hg\": true,\n    "
  },
  {
    "path": "LICENSE",
    "chars": 1139,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n\nPerm"
  },
  {
    "path": "api/messaging/.gitignore",
    "chars": 134,
    "preview": "node_modules\n\n# Lockfiles - See https://github.com/PlasmoHQ/p1asm0\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n\n.turbo\n\nk"
  },
  {
    "path": "api/messaging/LICENSE",
    "chars": 1139,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n\nPerm"
  },
  {
    "path": "api/messaging/jest.config.mjs",
    "chars": 364,
    "preview": "/**\n * @type {import('@jest/types').Config.InitialOptions}\n */\n\nconst config = {\n  clearMocks: true,\n  testEnvironment: "
  },
  {
    "path": "api/messaging/package.json",
    "chars": 3337,
    "preview": "{\n  \"name\": \"@plasmohq/messaging\",\n  \"version\": \"0.7.2\",\n  \"description\": \"Type-safe, zero-config messaging library for "
  },
  {
    "path": "api/messaging/src/background.ts",
    "chars": 698,
    "preview": "import type { PlasmoMessaging, PortName } from \"./index\"\nimport { getExtRuntime } from \"./utils\"\n\nexport const getPortMa"
  },
  {
    "path": "api/messaging/src/hook.ts",
    "chars": 1997,
    "preview": "import { useEffect, useRef, useState } from \"react\"\n\nimport { relayMessage, type MessageName, type PlasmoMessaging } fro"
  },
  {
    "path": "api/messaging/src/index.ts",
    "chars": 1802,
    "preview": "import { relay as rawRelay, sendViaRelay as rawSendViaRelay } from \"./relay\"\nimport type { MessageName, PlasmoMessaging "
  },
  {
    "path": "api/messaging/src/message.ts",
    "chars": 888,
    "preview": "import { type PlasmoMessaging } from \"./index\"\nimport { getExtRuntime } from \"./utils\"\n\nexport const listen = <RequestBo"
  },
  {
    "path": "api/messaging/src/port.ts",
    "chars": 936,
    "preview": "import type { PortName } from \"./index\"\nimport { getExtRuntime } from \"./utils\"\n\nconst portMap = new Map<PortName, chrom"
  },
  {
    "path": "api/messaging/src/pub-sub.ts",
    "chars": 1527,
    "preview": "import { getExtRuntime } from \"./utils\"\n\nexport type PubSubMessage = {\n  from?: number\n  to?: number\n  payload: any\n}\n\n/"
  },
  {
    "path": "api/messaging/src/relay.test.ts",
    "chars": 3383,
    "preview": "import { beforeEach, describe, expect, jest, test } from \"@jest/globals\"\n\nimport type { PlasmoMessaging } from \"./types\""
  },
  {
    "path": "api/messaging/src/relay.ts",
    "chars": 1867,
    "preview": "import { nanoid } from \"nanoid\"\n\nimport type { PlasmoMessaging } from \"./index\"\nimport { isSameOrigin } from \"./utils\"\n\n"
  },
  {
    "path": "api/messaging/src/types.ts",
    "chars": 2876,
    "preview": "export interface MessagesMetadata {}\nexport interface PortsMetadata {}\n\nexport type MessageName = keyof MessagesMetadata"
  },
  {
    "path": "api/messaging/src/utils.ts",
    "chars": 1232,
    "preview": "import type { PlasmoMessaging } from \"./index\"\n\nconst extTabs = (globalThis.browser?.tabs ||\n  globalThis.chrome?.tabs) "
  },
  {
    "path": "api/messaging/tsconfig.json",
    "chars": 494,
    "preview": "{\n  \"compilerOptions\": {\n    \"outDir\": \"dist\",\n    \"resolveJsonModule\": true,\n    \"sourceMap\": true,\n    \"strict\": false"
  },
  {
    "path": "api/persistent/.gitignore",
    "chars": 134,
    "preview": "node_modules\n\n# Lockfiles - See https://github.com/PlasmoHQ/p1asm0\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n\n.turbo\n\nk"
  },
  {
    "path": "api/persistent/LICENSE",
    "chars": 1139,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n\nPerm"
  },
  {
    "path": "api/persistent/README.md",
    "chars": 238,
    "preview": "# Plasmo Persistent runtime\n\nThis library contains a couple of hacks to keep the BGSW alive for MV3 transitioning.\n\nUsag"
  },
  {
    "path": "api/persistent/package.json",
    "chars": 1790,
    "preview": "{\n  \"name\": \"@plasmohq/persistent\",\n  \"version\": \"0.0.6\",\n  \"description\": \"A couple of hacks to keep the BGSW alive in "
  },
  {
    "path": "api/persistent/src/background.ts",
    "chars": 296,
    "preview": "export const keepAlive = () => {\n  const extRuntime = (globalThis.browser?.runtime ||\n    globalThis.chrome?.runtime) as"
  },
  {
    "path": "api/persistent/src/index.ts",
    "chars": 23,
    "preview": "export const life = 42\n"
  },
  {
    "path": "api/persistent/tsconfig.json",
    "chars": 73,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/utils\",\n  \"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "api/persistent/tsup.config.ts",
    "chars": 386,
    "preview": "import { defineConfig } from \"tsup\"\n\nexport default defineConfig((opt) => {\n  const isProd = !opt.watch\n  return {\n    e"
  },
  {
    "path": "api/selector/.gitignore",
    "chars": 134,
    "preview": "node_modules\n\n# Lockfiles - See https://github.com/PlasmoHQ/p1asm0\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n\n.turbo\n\nk"
  },
  {
    "path": "api/selector/LICENSE",
    "chars": 1139,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n\nPerm"
  },
  {
    "path": "api/selector/package.json",
    "chars": 2257,
    "preview": "{\n  \"name\": \"@plasmohq/selector\",\n  \"version\": \"0.0.7\",\n  \"description\": \"Powerful Selector API with Dedicated Monitorin"
  },
  {
    "path": "api/selector/src/background.ts",
    "chars": 1240,
    "preview": "import type { SelectorMessage } from \"./types\"\n\n// Simple cache, it won't persist, but it will do for now\nconst softCach"
  },
  {
    "path": "api/selector/src/hook.ts",
    "chars": 36,
    "preview": "export const useSelector = () => {}\n"
  },
  {
    "path": "api/selector/src/index.ts",
    "chars": 1143,
    "preview": "import type { SelectorMessage } from \"./types\"\n\nasync function sendInvalidSelectors(selectors: string[]) {\n  try {\n    r"
  },
  {
    "path": "api/selector/src/monitor.ts",
    "chars": 114,
    "preview": "export {}\n\ndocument.querySelector = new Proxy(document.querySelector, {\n  apply: (target, thisArg, args) => {}\n})\n"
  },
  {
    "path": "api/selector/src/types.ts",
    "chars": 125,
    "preview": "export type SelectorMessage = {\n  name: \"plasmo:selector:invalid\"\n  payload: {\n    selectors: string[]\n    url: string\n "
  },
  {
    "path": "api/selector/tsconfig.json",
    "chars": 514,
    "preview": "{\n  \"compilerOptions\": {\n    \"outDir\": \"dist\",\n    \"resolveJsonModule\": true,\n    \"sourceMap\": true,\n    \"strict\": false"
  },
  {
    "path": "api/selector/tsup.config.ts",
    "chars": 580,
    "preview": "import { defineConfig } from \"tsup\"\n\nexport default defineConfig((opt) => {\n  const isProd = !opt.watch\n  return {\n    e"
  },
  {
    "path": "cli/create-plasmo/bin/index.mjs",
    "chars": 47,
    "preview": "#!/usr/bin/env node\n\nimport \"../dist/index.js\"\n"
  },
  {
    "path": "cli/create-plasmo/package.json",
    "chars": 1187,
    "preview": "{\n  \"name\": \"create-plasmo\",\n  \"version\": \"0.90.5\",\n  \"description\": \"Create Plasmo Framework Browser Extension\",\n  \"mai"
  },
  {
    "path": "cli/create-plasmo/src/commands.ts",
    "chars": 35,
    "preview": "export const validCommandList = []\n"
  },
  {
    "path": "cli/create-plasmo/src/index.ts",
    "chars": 771,
    "preview": "#!/usr/bin/env node\nimport { argv, exit } from \"process\"\nimport { version } from \"plasmo/package.json\"\nimport init from "
  },
  {
    "path": "cli/create-plasmo/tsconfig.json",
    "chars": 308,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/cli.json\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\", \"templa"
  },
  {
    "path": "cli/plasmo/.eslintrc.js",
    "chars": 57,
    "preview": "module.exports = require(\"@plasmo/config/eslint-preset\")\n"
  },
  {
    "path": "cli/plasmo/LICENSE",
    "chars": 1139,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n\nPerm"
  },
  {
    "path": "cli/plasmo/README.md",
    "chars": 6922,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/bin/index.mjs",
    "chars": 47,
    "preview": "#!/usr/bin/env node\n\nimport \"../dist/index.js\"\n"
  },
  {
    "path": "cli/plasmo/i18n/README.de-DE.md",
    "chars": 5263,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.fr-FR.md",
    "chars": 5666,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.id-ID.md",
    "chars": 6965,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.ja-JP.md",
    "chars": 5779,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.ko-KR.md",
    "chars": 5936,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.ru-RU.md",
    "chars": 7159,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.tr-TR.md",
    "chars": 6905,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.vi-VN.md",
    "chars": 5213,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/i18n/README.zh-CN.md",
    "chars": 5572,
    "preview": "<p align=\"center\">\n  <a href=\"https://plasmo.com\">\n    <img alt=\"plasmo logo\" width=\"75%\" src=\"https://www.plasmo.com/as"
  },
  {
    "path": "cli/plasmo/index.mjs",
    "chars": 1045,
    "preview": "import { argv, exit } from \"process\"\nimport { build, context } from \"esbuild\"\nimport fse from \"fs-extra\"\n\nconst watch = "
  },
  {
    "path": "cli/plasmo/package.json",
    "chars": 2032,
    "preview": "{\n  \"name\": \"plasmo\",\n  \"version\": \"0.90.5\",\n  \"description\": \"The Plasmo Framework CLI\",\n  \"publishConfig\": {\n    \"type"
  },
  {
    "path": "cli/plasmo/src/commands/build.ts",
    "chars": 1544,
    "preview": "import { getNonFlagArgvs } from \"@plasmo/utils/argv\"\nimport { hasFlag } from \"@plasmo/utils/flags\"\nimport { iLog, sLog }"
  },
  {
    "path": "cli/plasmo/src/commands/dev.ts",
    "chars": 3808,
    "preview": "import {\n  BuildSocketEvent,\n  getBuildSocket\n} from \"@plasmo/framework-shared/build-socket\"\nimport { getFlag, isVerbose"
  },
  {
    "path": "cli/plasmo/src/commands/help.ts",
    "chars": 143,
    "preview": "import { printHeader, printHelp } from \"~features/helpers/print\"\n\nasync function help() {\n  printHeader()\n  printHelp()\n"
  },
  {
    "path": "cli/plasmo/src/commands/index.ts",
    "chars": 556,
    "preview": "export const runMap = {\n  help: () => import(\"./help\"),\n\n  //#ifdef !IS_BINARY\n  start: () => import(\"./start\"),\n  init:"
  },
  {
    "path": "cli/plasmo/src/commands/init.ts",
    "chars": 1898,
    "preview": "import { resolve } from \"path\"\nimport { cwd } from \"process\"\nimport { kebabCase } from \"change-case\"\n\nimport { hasFlag }"
  },
  {
    "path": "cli/plasmo/src/commands/package.ts",
    "chars": 808,
    "preview": "import { hasFlag } from \"@plasmo/utils/flags\"\nimport { iLog } from \"@plasmo/utils/logging\"\n\nimport { getBundleConfig } f"
  },
  {
    "path": "cli/plasmo/src/commands/start.ts",
    "chars": 140,
    "preview": "import { iLog } from \"@plasmo/utils/logging\"\n\nasync function start() {\n  iLog(\"Start the extension development...\")\n}\n\ne"
  },
  {
    "path": "cli/plasmo/src/commands/version.ts",
    "chars": 92,
    "preview": "async function version() {\n  console.log(process.env.APP_VERSION)\n}\n\nexport default version\n"
  },
  {
    "path": "cli/plasmo/src/features/background-service-worker/bgsw-entry.ts",
    "chars": 1020,
    "preview": "import { relative, resolve } from \"path\"\nimport { ensureDir, outputFile } from \"fs-extra\"\n\nimport { vLog } from \"@plasmo"
  },
  {
    "path": "cli/plasmo/src/features/background-service-worker/bgsw-main-world-script.ts",
    "chars": 2041,
    "preview": "import { relative, resolve } from \"path\"\nimport { camelCase } from \"change-case\"\nimport { outputFile } from \"fs-extra\"\n\n"
  },
  {
    "path": "cli/plasmo/src/features/background-service-worker/bgsw-messaging-declaration.ts",
    "chars": 848,
    "preview": "import { resolve } from \"path\"\nimport { outputFile } from \"fs-extra\"\n\nimport type { CommonPath } from \"~features/extensi"
  },
  {
    "path": "cli/plasmo/src/features/background-service-worker/bgsw-messaging.ts",
    "chars": 4693,
    "preview": "import { camelCase } from \"change-case\"\nimport glob from \"fast-glob\"\nimport { outputFile } from \"fs-extra\"\nimport { join"
  },
  {
    "path": "cli/plasmo/src/features/background-service-worker/update-bgsw-entry.ts",
    "chars": 1115,
    "preview": "import { find } from \"@plasmo/utils/array\"\nimport { isAccessible } from \"@plasmo/utils/fs\"\n\nimport { createBgswEntry } f"
  },
  {
    "path": "cli/plasmo/src/features/env/env-config.ts",
    "chars": 4217,
    "preview": "// Forked from https://github.com/vercel/next.js/blob/canary/packages/next-env/index.ts\nimport { readFile } from \"fs/pro"
  },
  {
    "path": "cli/plasmo/src/features/env/env-declaration.ts",
    "chars": 773,
    "preview": "import { resolve } from \"path\"\nimport { outputFile } from \"fs-extra\"\n\nimport type { PlasmoManifest } from \"~features/man"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/common-path.ts",
    "chars": 1831,
    "preview": "import { existsSync } from \"fs\"\nimport { basename, resolve } from \"path\"\nimport { cwd } from \"process\"\n\nimport { getFlag"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/content-script-config.ts",
    "chars": 1923,
    "preview": "import { readFile } from \"fs/promises\"\nimport typescript, { type Node, type VariableDeclaration } from \"typescript\"\n\nimp"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/generate-icons.ts",
    "chars": 3828,
    "preview": "import { basename, resolve } from \"path\"\nimport { copy, ensureDir } from \"fs-extra\"\nimport sharp from \"sharp\"\n\nimport { "
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/get-bundle-config.ts",
    "chars": 388,
    "preview": "import { getFlagMap } from \"~features/helpers/flag\"\n\nexport const getBundleConfig = () => {\n  const flagMap = getFlagMap"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/git-ignore.ts",
    "chars": 520,
    "preview": "export const generateGitIgnore = () => `\n# See https://help.github.com/articles/ignoring-files/ for more about ignoring "
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/package-file.ts",
    "chars": 2473,
    "preview": "import { userInfo } from \"os\"\nimport { sentenceCase } from \"change-case\"\nimport getPackageJson, { type AbbreviatedVersio"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/parse-ast.ts",
    "chars": 1715,
    "preview": "/**\n * Copyright (c) Plasmo Corp, foss@plasmo.com, MIT Licensed\n * ---\n * Adapted from https://github.com/dword-design/t"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/project-path.ts",
    "chars": 5450,
    "preview": "import { resolve } from \"path\"\n\nimport { getEnvFileNames } from \"~features/env/env-config\"\nimport type { SupportedUiExt "
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/project-watcher.ts",
    "chars": 4715,
    "preview": "import { subscribe, type Event } from \"@parcel/watcher\"\n\nimport { PARCEL_WATCHER_BACKEND } from \"@plasmo/constants/misc\""
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/strip-underscore.ts",
    "chars": 875,
    "preview": "import { readdir, readFile, rename, stat, writeFile } from \"fs/promises\"\nimport { join, resolve } from \"path\"\n\nconst str"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/template-path.ts",
    "chars": 779,
    "preview": "import { dirname, resolve } from \"path\"\nimport { fileURLToPath } from \"url\"\n\nexport const getTemplatePath = () => {\n  co"
  },
  {
    "path": "cli/plasmo/src/features/extension-devtools/tsconfig.ts",
    "chars": 1406,
    "preview": "import { readFile } from \"fs/promises\"\nimport { resolve } from \"path\"\nimport { outputFile, outputJson } from \"fs-extra\"\n"
  },
  {
    "path": "cli/plasmo/src/features/extra/cache-busting.ts",
    "chars": 1200,
    "preview": "import { lstat } from \"fs/promises\"\nimport { resolve } from \"path\"\nimport { emptyDir, ensureDir } from \"fs-extra\"\n\nimpor"
  },
  {
    "path": "cli/plasmo/src/features/extra/next-new-tab.ts",
    "chars": 2277,
    "preview": "import { mkdir } from \"fs/promises\"\nimport { resolve } from \"path\"\nimport { sentenceCase } from \"change-case\"\nimport { c"
  },
  {
    "path": "cli/plasmo/src/features/framework-update/version-tracker.ts",
    "chars": 2705,
    "preview": "import { readJson, writeJson } from \"fs-extra\"\nimport getPackageJson, { type AbbreviatedVersion } from \"package-json\"\nim"
  },
  {
    "path": "cli/plasmo/src/features/helpers/create-parcel-bundler.ts",
    "chars": 3423,
    "preview": "import { dirname, join, resolve } from \"path\"\nimport ParcelFS from \"@parcel/fs\"\nimport ParcelPM from \"@parcel/package-ma"
  },
  {
    "path": "cli/plasmo/src/features/helpers/crypto.ts",
    "chars": 303,
    "preview": "import { createHash } from \"crypto\"\n\n/**\n * Fast hash for local file revving\n * DO NOT USE FOR SENSITIVE PURPOSES\n * md5"
  },
  {
    "path": "cli/plasmo/src/features/helpers/flag.ts",
    "chars": 1687,
    "preview": "import { kebabCase } from \"change-case\"\n\nimport { getFlag } from \"@plasmo/utils/flags\"\n\nexport const getFlagMap = () => "
  },
  {
    "path": "cli/plasmo/src/features/helpers/loading-animation.ts",
    "chars": 810,
    "preview": "const LOADING_TEXT = \"🔄 Building\"\nconst state = {\n  loadingInterval: null as NodeJS.Timeout | null,\n  isLoading: false,\n"
  },
  {
    "path": "cli/plasmo/src/features/helpers/package-manager.ts",
    "chars": 1036,
    "preview": "/**\n * Forked from https://github.com/vercel/next.js/blob/canary/packages/create-next-app/helpers/get-pkg-manager.ts\n */"
  },
  {
    "path": "cli/plasmo/src/features/helpers/print.ts",
    "chars": 396,
    "preview": "import { cLog } from \"@plasmo/utils/logging\"\n\nimport { validCommandList } from \"~commands\"\nimport { flagHelp } from \"~fe"
  },
  {
    "path": "cli/plasmo/src/features/helpers/prompt.ts",
    "chars": 259,
    "preview": "import inquirer from \"inquirer\"\n\nexport const quickPrompt = async (label = \"\", defaultValue = \"\") => {\n  const { data } "
  },
  {
    "path": "cli/plasmo/src/features/helpers/traverse.ts",
    "chars": 915,
    "preview": "import { iLog } from \"@plasmo/utils/logging\"\n\nconst defaultTransformer = (target: any) =>\n  iLog({\n    target\n  })\n\n/**\n"
  },
  {
    "path": "cli/plasmo/src/features/manifest-factory/base.ts",
    "chars": 18369,
    "preview": "import { ok } from \"assert\"\nimport { readdir } from \"fs/promises\"\nimport {\n  basename,\n  dirname,\n  extname,\n  isAbsolut"
  },
  {
    "path": "cli/plasmo/src/features/manifest-factory/create-manifest.ts",
    "chars": 1966,
    "preview": "import { find } from \"@plasmo/utils/array\"\nimport { isAccessible } from \"@plasmo/utils/fs\"\nimport { vLog, wLog } from \"@"
  },
  {
    "path": "cli/plasmo/src/features/manifest-factory/mv2.ts",
    "chars": 2552,
    "preview": "import type {\n  ExtensionManifestV2,\n  ExtensionManifestV3\n} from \"@plasmo/constants\"\nimport { iLog } from \"@plasmo/util"
  },
  {
    "path": "cli/plasmo/src/features/manifest-factory/mv3.ts",
    "chars": 2160,
    "preview": "import type { ExtensionManifestV3 } from \"@plasmo/constants\"\n\nimport type { PlasmoBundleConfig } from \"~features/extensi"
  },
  {
    "path": "cli/plasmo/src/features/manifest-factory/scaffolder.ts",
    "chars": 7156,
    "preview": "import { readFile, writeFile } from \"fs/promises\"\nimport { join, relative, resolve, type ParsedPath } from \"path\"\nimport"
  },
  {
    "path": "cli/plasmo/src/features/manifest-factory/ui-library.ts",
    "chars": 3242,
    "preview": "import { resolve } from \"path\"\nimport { cwd } from \"process\"\nimport { readJson } from \"fs-extra\"\nimport semver from \"sem"
  },
  {
    "path": "cli/plasmo/src/features/manifest-factory/zip.ts",
    "chars": 2069,
    "preview": "import { createReadStream, createWriteStream } from \"fs\"\nimport { resolve } from \"path\"\nimport glob from \"fast-glob\"\nimp"
  },
  {
    "path": "cli/plasmo/src/features/project-creator/from-existing-manifest.ts",
    "chars": 5484,
    "preview": "import { readFile } from \"fs/promises\"\nimport { extname } from \"path\"\nimport { strFromU8, unzipSync, type Unzipped } fro"
  },
  {
    "path": "cli/plasmo/src/features/project-creator/get-raw-name.ts",
    "chars": 539,
    "preview": "import { createQuestId } from \"mnemonic-id\"\n\nimport { getNonFlagArgvs } from \"@plasmo/utils/argv\"\nimport { vLog } from \""
  },
  {
    "path": "cli/plasmo/src/features/project-creator/git-init.ts",
    "chars": 1413,
    "preview": "import spawnAsync, { type SpawnOptions } from \"@expo/spawn-async\"\n\nimport { isAccessible } from \"@plasmo/utils/fs\"\nimpor"
  },
  {
    "path": "cli/plasmo/src/features/project-creator/index.ts",
    "chars": 7149,
    "preview": "import { existsSync } from \"fs\"\nimport { lstat, readFile, writeFile } from \"fs/promises\"\nimport { isAbsolute, join, rela"
  },
  {
    "path": "cli/plasmo/src/features/project-creator/install-dependencies.ts",
    "chars": 506,
    "preview": "import spawnAsync from \"@expo/spawn-async\"\n\nimport { iLog, wLog } from \"@plasmo/utils/logging\"\n\nimport type { PackageMan"
  },
  {
    "path": "cli/plasmo/src/features/project-creator/print-ready.ts",
    "chars": 827,
    "preview": "import { sLog } from \"@plasmo/utils/logging\"\n\nimport type { CommonPath } from \"~features/extension-devtools/common-path\""
  },
  {
    "path": "cli/plasmo/src/index.ts",
    "chars": 1329,
    "preview": "#!/usr/bin/env node\nimport { argv, exit, versions } from \"process\"\nimport semver from \"semver\"\n\nimport { ErrorMessage } "
  },
  {
    "path": "cli/plasmo/src/type.ts",
    "chars": 4045,
    "preview": "import * as React from 'react';\nimport type { Root } from \"react-dom/client\"\n\nimport type { ManifestContentScript } from"
  },
  {
    "path": "cli/plasmo/templates/plasmo.d.ts",
    "chars": 1565,
    "preview": "declare namespace NodeJS {\n  interface ProcessEnv {\n    NODE_ENV: \"development\" | \"production\"\n\n    PLASMO_BROWSER?:\n   "
  },
  {
    "path": "cli/plasmo/templates/static/background/index.ts",
    "chars": 42,
    "preview": "import \"./messaging\"\nimport \"~background\"\n"
  },
  {
    "path": "cli/plasmo/templates/static/common/csui-container-react.tsx",
    "chars": 1618,
    "preview": "import React from \"react\"\n\nimport type { PlasmoCSUIContainerProps } from \"~type\"\n\nexport const OverlayCSUIContainer = (p"
  },
  {
    "path": "cli/plasmo/templates/static/common/csui-container-vanilla.tsx",
    "chars": 1307,
    "preview": "import type { PlasmoCSUIContainerProps } from \"~type\"\n\nexport const createOverlayCSUIContainer = (props: PlasmoCSUIConta"
  },
  {
    "path": "cli/plasmo/templates/static/common/csui.ts",
    "chars": 8618,
    "preview": "import type { PlasmoCSUI, PlasmoCSUIAnchor, PlasmoCSUIMountState } from \"~type\"\n\nasync function createShadowDOM<T>(Mount"
  },
  {
    "path": "cli/plasmo/templates/static/common/react.ts",
    "chars": 304,
    "preview": "import { Fragment, type FC, type ReactNode } from \"react\"\n\nexport const getLayout = (RawImport: any): FC<{ children: Rea"
  },
  {
    "path": "cli/plasmo/templates/static/common/vue.ts",
    "chars": 170,
    "preview": "globalThis.__VUE_OPTIONS_API__ = true\nglobalThis.__VUE_PROD_DEVTOOLS__ = process.env.NODE_ENV !== \"production\"\nglobalThi"
  },
  {
    "path": "cli/plasmo/templates/static/react17/content-script-ui-mount.tsx",
    "chars": 2170,
    "preview": "import React from \"react\"\nimport * as ReactDOM from \"react-dom\"\n\nimport { createAnchorObserver, createRender } from \"@pl"
  },
  {
    "path": "cli/plasmo/templates/static/react17/index.html",
    "chars": 322,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>__plasmo_static_index_title__</title>\n    <meta charset=\"utf-8\" />\n    <meta "
  },
  {
    "path": "cli/plasmo/templates/static/react17/index.tsx",
    "chars": 515,
    "preview": "import React from \"react\"\nimport * as ReactDOM from \"react-dom\"\n\nimport { getLayout } from \"@plasmo-static-common/react\""
  },
  {
    "path": "cli/plasmo/templates/static/react18/content-script-ui-mount.tsx",
    "chars": 2187,
    "preview": "import React from \"react\"\nimport { createRoot } from \"react-dom/client\"\n\nimport { createAnchorObserver, createRender } f"
  },
  {
    "path": "cli/plasmo/templates/static/react18/index.html",
    "chars": 322,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>__plasmo_static_index_title__</title>\n    <meta charset=\"utf-8\" />\n    <meta "
  },
  {
    "path": "cli/plasmo/templates/static/react18/index.tsx",
    "chars": 542,
    "preview": "import React from \"react\"\nimport { createRoot } from \"react-dom/client\"\n\nimport { getLayout } from \"@plasmo-static-commo"
  },
  {
    "path": "cli/plasmo/templates/static/react19/content-script-ui-mount.tsx",
    "chars": 2187,
    "preview": "import React from \"react\"\nimport { createRoot } from \"react-dom/client\"\n\nimport { createAnchorObserver, createRender } f"
  },
  {
    "path": "cli/plasmo/templates/static/react19/index.html",
    "chars": 322,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>__plasmo_static_index_title__</title>\n    <meta charset=\"utf-8\" />\n    <meta "
  },
  {
    "path": "cli/plasmo/templates/static/react19/index.tsx",
    "chars": 542,
    "preview": "import React from \"react\"\nimport { createRoot } from \"react-dom/client\"\n\nimport { getLayout } from \"@plasmo-static-commo"
  },
  {
    "path": "cli/plasmo/templates/static/svelte4/content-script-ui-mount.ts",
    "chars": 1944,
    "preview": "import { createAnchorObserver, createRender } from \"@plasmo-static-common/csui\"\nimport {\n  createInlineCSUIContainer,\n  "
  },
  {
    "path": "cli/plasmo/templates/static/svelte4/index.html",
    "chars": 322,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>__plasmo_static_index_title__</title>\n    <meta charset=\"utf-8\" />\n    <meta "
  },
  {
    "path": "cli/plasmo/templates/static/svelte4/index.ts",
    "chars": 314,
    "preview": "// @ts-nocheck\nimport * as Component from \"__plasmo_import_module__\"\n\nlet __plasmoRoot: HTMLElement = null\n\ndocument.add"
  },
  {
    "path": "cli/plasmo/templates/static/vanilla/index.html",
    "chars": 322,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>__plasmo_static_index_title__</title>\n    <meta charset=\"utf-8\" />\n    <meta "
  },
  {
    "path": "cli/plasmo/templates/static/vanilla/index.ts",
    "chars": 49,
    "preview": "// @ts-nocheck\nimport \"__plasmo_import_module__\"\n"
  },
  {
    "path": "cli/plasmo/templates/static/vue3/content-script-ui-mount.ts",
    "chars": 2426,
    "preview": "import { createApp } from \"vue\"\n\nimport { createAnchorObserver, createRender } from \"@plasmo-static-common/csui\"\nimport "
  },
  {
    "path": "cli/plasmo/templates/static/vue3/index.html",
    "chars": 322,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>__plasmo_static_index_title__</title>\n    <meta charset=\"utf-8\" />\n    <meta "
  },
  {
    "path": "cli/plasmo/templates/static/vue3/index.ts",
    "chars": 298,
    "preview": "import { createApp } from \"vue\"\n\n// @ts-ignore\nimport * as Component from \"__plasmo_import_module__\"\n\nimport \"@plasmo-st"
  },
  {
    "path": "cli/plasmo/templates/tsconfig.base.json",
    "chars": 729,
    "preview": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"display\": \"Plasmo Extension\",\n  \"files\": [\"./plasmo.d.ts\"],\n "
  },
  {
    "path": "cli/plasmo/tsconfig.json",
    "chars": 457,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/framework.json\",\n  \"include\": [\n    \"src/**/*.ts\",\n    \"templates/plasmo.d.ts\",\n    \"t"
  },
  {
    "path": "core/parcel-bundler/.gitignore",
    "chars": 19,
    "preview": "node_modules\n\ndist/"
  },
  {
    "path": "core/parcel-bundler/package.json",
    "chars": 846,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-bundler\",\n  \"version\": \"0.5.6\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n "
  },
  {
    "path": "core/parcel-bundler/src/bit-set.ts",
    "chars": 2217,
    "preview": "import nullthrows from \"nullthrows\"\n\nconst BIGINT_ZERO = 0n\nconst BIGINT_ONE = 1n\nlet numberToBigInt = (v: number): bigi"
  },
  {
    "path": "core/parcel-bundler/src/can-merge.ts",
    "chars": 308,
    "preview": "export function canMerge(a, b) {\n  // Bundles can be merged if they have the same type and environment,\n  // unless they"
  },
  {
    "path": "core/parcel-bundler/src/create-bundle.ts",
    "chars": 1223,
    "preview": "import type { Asset, BundleBehavior, Environment, Target } from \"@parcel/types\"\nimport nullthrows from \"nullthrows\"\n\nimp"
  },
  {
    "path": "core/parcel-bundler/src/create-ideal-graph.ts",
    "chars": 37019,
    "preview": "import invariant from \"assert\"\nimport { ALL_EDGE_TYPES, ContentGraph, Graph, NodeId } from \"@parcel/graph\"\nimport type {"
  },
  {
    "path": "core/parcel-bundler/src/decorate-legacy-graph.ts",
    "chars": 5972,
    "preview": "import invariant from \"assert\"\nimport { ALL_EDGE_TYPES, NodeId } from \"@parcel/graph\"\nimport type {\n  BundleGroup,\n  Bun"
  },
  {
    "path": "core/parcel-bundler/src/get-entry-by-target.ts",
    "chars": 930,
    "preview": "// @ts-nocheck\nimport invariant from \"assert\"\nimport type { Asset, Dependency, MutableBundleGraph } from \"@parcel/types\""
  },
  {
    "path": "core/parcel-bundler/src/get-reachable-bundle-root.ts",
    "chars": 294,
    "preview": "import nullthrows from \"nullthrows\"\n\nimport type { BundleRoot } from \"./types\"\n\nexport function getReachableBundleRoots("
  },
  {
    "path": "core/parcel-bundler/src/index.ts",
    "chars": 2170,
    "preview": "/**\n * Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n * MIT License\n *\n * "
  },
  {
    "path": "core/parcel-bundler/src/remove-bundle.ts",
    "chars": 954,
    "preview": "import invariant from \"assert\"\nimport type { Graph, NodeId } from \"@parcel/graph\"\nimport type { Asset, Dependency } from"
  },
  {
    "path": "core/parcel-bundler/src/types.ts",
    "chars": 1492,
    "preview": "import type { ContentGraph, Graph, NodeId } from \"@parcel/graph\"\nimport type {\n  Asset,\n  BundleBehavior,\n  Dependency,\n"
  },
  {
    "path": "core/parcel-bundler/tsconfig.json",
    "chars": 116,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/framework\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\"]\n}\n"
  },
  {
    "path": "core/parcel-compressor-utf8/.gitignore",
    "chars": 19,
    "preview": "node_modules\n\ndist/"
  },
  {
    "path": "core/parcel-compressor-utf8/package.json",
    "chars": 739,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-compressor-utf8\",\n  \"version\": \"0.1.1\",\n  \"description\": \"Plasmo UTF8 Compressor for Exten"
  },
  {
    "path": "core/parcel-compressor-utf8/src/index.ts",
    "chars": 349,
    "preview": "/**\n * Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n * MIT License\n */\nim"
  },
  {
    "path": "core/parcel-compressor-utf8/src/utf8-transform.ts",
    "chars": 1249,
    "preview": "/**\n * Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n * MIT License\n */\nim"
  },
  {
    "path": "core/parcel-compressor-utf8/tsconfig.json",
    "chars": 116,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/framework\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\"]\n}\n"
  },
  {
    "path": "core/parcel-compressor-utf8/tsup.config.ts",
    "chars": 96,
    "preview": "import { defineConfig } from \"tsup\"\n\nexport default defineConfig({\n  entry: [\"src/index.ts\"]\n})\n"
  },
  {
    "path": "core/parcel-config/.gitignore",
    "chars": 8,
    "preview": "run.json"
  },
  {
    "path": "core/parcel-config/index.json",
    "chars": 2302,
    "preview": "{\n  \"extends\": \"@parcel/config-default\",\n  \"bundler\": \"@plasmohq/parcel-bundler\",\n  \"resolvers\": [\n    \"@plasmohq/parcel"
  },
  {
    "path": "core/parcel-config/package.json",
    "chars": 1867,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-config\",\n  \"version\": \"0.42.0\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n "
  },
  {
    "path": "core/parcel-core/.gitignore",
    "chars": 19,
    "preview": "node_modules\n\ndist/"
  },
  {
    "path": "core/parcel-core/package.json",
    "chars": 1282,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-core\",\n  \"version\": \"0.1.11\",\n  \"description\": \"Plasmo Parcel Core Fork\",\n  \"files\": [\n   "
  },
  {
    "path": "core/parcel-core/src/index.ts",
    "chars": 12734,
    "preview": "/**\n * Forked from https://github.com/parcel-bundler/parcel/blob/19fe7ff00f28f44300fe803c4e594b9fc02b25ad/packages/core/"
  },
  {
    "path": "core/parcel-core/src/resolve-options.ts",
    "chars": 7426,
    "preview": "/**\n * Based on https://github.com/parcel-bundler/parcel/blob/v2/packages/core/core/src/resolveOptions.js\n * MIT License"
  },
  {
    "path": "core/parcel-core/src/types.ts",
    "chars": 109,
    "preview": "export type { InitialParcelOptions as ParcelOptions } from \"@parcel/types\"\n\nexport { Parcel } from \"./index\"\n"
  },
  {
    "path": "core/parcel-core/tsconfig.json",
    "chars": 116,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/framework\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\"]\n}\n"
  },
  {
    "path": "core/parcel-core/tsup.config.ts",
    "chars": 96,
    "preview": "import { defineConfig } from \"tsup\"\n\nexport default defineConfig({\n  entry: [\"src/index.ts\"]\n})\n"
  },
  {
    "path": "core/parcel-namer-manifest/.gitignore",
    "chars": 19,
    "preview": "node_modules\n\ndist/"
  },
  {
    "path": "core/parcel-namer-manifest/package.json",
    "chars": 824,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-namer-manifest\",\n  \"version\": \"0.3.12\",\n  \"description\": \"Plasmo Parcel Namer for Extensio"
  },
  {
    "path": "core/parcel-namer-manifest/src/index.ts",
    "chars": 606,
    "preview": "/**\n * Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n * MIT License\n */\nim"
  },
  {
    "path": "core/parcel-namer-manifest/tsconfig.json",
    "chars": 110,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/cli\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\"]\n}\n"
  },
  {
    "path": "core/parcel-optimizer-encapsulate/.gitignore",
    "chars": 19,
    "preview": "node_modules\n\ndist/"
  },
  {
    "path": "core/parcel-optimizer-encapsulate/package.json",
    "chars": 848,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-optimizer-encapsulate\",\n  \"version\": \"0.0.8\",\n  \"description\": \"Plasmo ECMAScript Encapsul"
  },
  {
    "path": "core/parcel-optimizer-encapsulate/src/index.ts",
    "chars": 1447,
    "preview": "/**\n * Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n * MIT License\n *\n */"
  },
  {
    "path": "core/parcel-optimizer-encapsulate/tsconfig.json",
    "chars": 116,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/framework\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\"]\n}\n"
  },
  {
    "path": "core/parcel-optimizer-encapsulate/tsup.config.ts",
    "chars": 96,
    "preview": "import { defineConfig } from \"tsup\"\n\nexport default defineConfig({\n  entry: [\"src/index.ts\"]\n})\n"
  },
  {
    "path": "core/parcel-optimizer-es/.gitignore",
    "chars": 19,
    "preview": "node_modules\n\ndist/"
  },
  {
    "path": "core/parcel-optimizer-es/package.json",
    "chars": 890,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-optimizer-es\",\n  \"version\": \"0.4.2\",\n  \"description\": \"Plasmo ECMAScript Optimizer for Ext"
  },
  {
    "path": "core/parcel-optimizer-es/src/blob-to-string.ts",
    "chars": 726,
    "preview": "import { Buffer } from \"buffer\"\nimport { Readable } from \"stream\"\n\ntype Blob = Buffer | Readable | string\n\nexport functi"
  },
  {
    "path": "core/parcel-optimizer-es/src/index.ts",
    "chars": 2540,
    "preview": "/**\n * Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n * MIT License\n *\n * "
  },
  {
    "path": "core/parcel-optimizer-es/tsconfig.json",
    "chars": 116,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/framework\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\"]\n}\n"
  },
  {
    "path": "core/parcel-optimizer-es/tsup.config.ts",
    "chars": 96,
    "preview": "import { defineConfig } from \"tsup\"\n\nexport default defineConfig({\n  entry: [\"src/index.ts\"]\n})\n"
  },
  {
    "path": "core/parcel-packager/.gitignore",
    "chars": 19,
    "preview": "node_modules\n\ndist/"
  },
  {
    "path": "core/parcel-packager/package.json",
    "chars": 928,
    "preview": "{\n  \"name\": \"@plasmohq/parcel-packager\",\n  \"version\": \"0.6.15\",\n  \"description\": \"Plasmo Parcel Packager for Web Extensi"
  },
  {
    "path": "core/parcel-packager/src/get-web-accessible-resources.ts",
    "chars": 2939,
    "preview": "import type {\n  BundleGraph,\n  Dependency,\n  NamedBundle,\n  PluginOptions\n} from \"@parcel/types\"\nimport nullthrows from "
  },
  {
    "path": "core/parcel-packager/src/index.ts",
    "chars": 1765,
    "preview": "/**\n * Copyright (c) 2023 Plasmo Corp. <foss@plasmo.com> (https://www.plasmo.com) and contributors\n * MIT License\n *\n * "
  },
  {
    "path": "core/parcel-packager/src/utils.ts",
    "chars": 253,
    "preview": "import type { NamedBundle } from \"@parcel/types\"\nimport { relativeBundlePath } from \"@parcel/utils\"\n\nexport function get"
  },
  {
    "path": "core/parcel-packager/tsconfig.json",
    "chars": 116,
    "preview": "{\n  \"extends\": \"@plasmo/config/ts/framework\",\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"dist\", \"node_modules\"]\n}\n"
  }
]

// ... and 134 more files (download for full content)

About this extraction

This page contains the full source code of the PlasmoHQ/plasmo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 334 files (506.4 KB), approximately 145.3k tokens, and a symbol index with 376 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!