Chrome Extension: React Cannot use import statement outside a module only on Chrome Extension Store

193 views
Skip to first unread message

Benjamin

unread,
Oct 14, 2024, 4:26:28 AM10/14/24
to Chromium Extensions
I'm developing a Chrome extension using React and Vite. When I load the extension locally from the dist folder as an unpacked extension in Chrome, it runs without any issues.
However, after publishing it to the Chrome Web Store, I receive the following error when trying to use the extension:

Uncaught SyntaxError: Cannot use import statement outside a module

What I've tried so far:
  • List item
  • Removed React.StrictMode from the code.
  • Modified tsconfig.json with different module settings:
  • Tried "module": "ESNext"
  • Tried "module": "commonjs"
  • Ensured that package.json includes "type": "module".
  • Built the project using vite build instead of vite dev; no errors were shown during the build process.

Content Script Code

import ReactDOM from 'react-dom/client'; import ContentApp from './ContentApp'; import { ExplanationContainer } from '../features/explanation/ExplanationContainer'; import { ConfigProvider } from 'antd'; import customTheme from '../theme/customTheme.ts'; import '../index.css'; import { StyleProvider } from '@ant-design/cssinjs'; import { SessionProvider } from '../features/auth/SessionContext.tsx'; import { getQuizProgression } from '../features/shared/helpers/getQuizProgression.ts'; const ROOT_ELEMENT_ID = 'crx-root'; const EXPLANATION_ROOT_ID = 'explanation-root'; interface RootInfo { root: ReactDOM.Root; element: HTMLElement; } let contentRoot: RootInfo | null = null; let explanationRoot: RootInfo | null = null; let isRendering = false; const AppProviders: React.FC<{ children: React.ReactNode; }> = ({ children }) => { return ( <StyleProvider hashPriority="high"> <ConfigProvider prefixCls={'ant'} theme={customTheme}> <SessionProvider>{children}</SessionProvider> </ConfigProvider> </StyleProvider> ); }; const createOrGetRoot = (id: string): HTMLElement => { let element = document.getElementById(id); if (!element) { element = document.createElement('div'); element.id = id; document.body.appendChild(element); } return element; }; const renderComponent = (id: string, Component: React.FC): RootInfo => { const element = createOrGetRoot(id); const root = ReactDOM.createRoot(element); root.render( <AppProviders> <Component /> </AppProviders> ); return { root, element }; }; const safeAppendChild = (parent: Element | null, child: HTMLElement) => { if (parent && !parent.contains(child)) { parent.appendChild(child); } }; const renderContentApp = () => { const cardHeader = document.querySelector('.card-header'); const cardHeaderText = cardHeader?.textContent; const keywords = ['Réglages', 'Settings', 'Einstellungen', 'Impostazioni']; const shouldRenderProfile = keywords.some((keyword) => cardHeaderText?.includes(keyword) ); const headerElement = document.querySelector('.card-body'); if (headerElement && shouldRenderProfile) { if (contentRoot) { safeAppendChild(headerElement, contentRoot.element); } else { contentRoot = renderComponent(ROOT_ELEMENT_ID, ContentApp); safeAppendChild(headerElement, contentRoot.element); } } else if (contentRoot) { contentRoot.root.unmount(); contentRoot = null; } }; const renderAiExplanation = () => { const bodyElement = document.querySelector('.card-body'); const quizProgressionText = getQuizProgression(); const shouldRenderExplanation = quizProgressionText !== null; if (shouldRenderExplanation && bodyElement) { if (explanationRoot == null) { explanationRoot = renderComponent( EXPLANATION_ROOT_ID, ExplanationContainer ); safeAppendChild(bodyElement, explanationRoot.element); } } else if (explanationRoot) { explanationRoot.root.unmount(); explanationRoot = null; } }; const renderComponents = () => { if (isRendering) { return; } isRendering = true; renderContentApp(); renderAiExplanation(); isRendering = false; }; const cleanup = () => { if (contentRoot) { contentRoot.root.unmount(); contentRoot = null; } if (explanationRoot) { explanationRoot.root.unmount(); explanationRoot = null; } }; const observeDocumentBody = () => { const observer = new MutationObserver(() => { renderComponents(); }); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('beforeunload', () => { observer.disconnect(); cleanup(); }); return observer; }; renderComponents(); const observer = observeDocumentBody(); if (import.meta.hot) { import.meta.hot.dispose(() => { observer.disconnect(); cleanup(); }); }

Here is my current manifest:

{ "manifest_version": 3, "version": "0.2.6", "name": "Paragliding Copilot AI", "description": "Enhance SHV FSVL eLearning experience with AI-generated explanations for questions, providing deeper understanding and insights.", "permissions": [ "tabs", "storage" ], "action": { "default_icon": "src/assets/para-bot-no-bg.png", "16": "src/assets/para-bot-no-bg-16.png", "32": "src/assets/para-bot-no-bg-32.png", "48": "src/assets/para-bot-no-bg-48.png", "128": "src/assets/para-bot-no-bg-128.png" }, "background": { "service_worker": "./src/background/background.ts" }, "content_scripts": [ { "js": [ "./src/content/content.tsx" ], "matches": [ "https://elearning.shv-fsvl.ch/*" ] } ], "web_accessible_resources": [ { "resources": [ "./src/assets/fonts/*" ], "matches": [ "*://*/*" ] } ] }

Additional Information:

Comments:
  • Is there a difference in how modules are handled when an extension is published versus when it's loaded locally?
  • Could there be an issue with the way Vite bundles the extension for production?
  • Are there specific configurations required for React-based Chrome extensions when publishing?

Any help would be greatly appreciated!

Patrick Kettner

unread,
Oct 14, 2024, 12:49:40 PM10/14/24
to Benjamin, Chromium Extensions
When I download the extension published on the store and load it locally, it also does not run.
It looks like you uploaded the project directory, rather than the generated `dist` directory. After downloading what you uploaded, running `npm install && npm run build`, the generated `dist` directory is a valid extension.

--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/aaefed51-e0b5-4fb1-9e1f-eff640f5505cn%40chromium.org.

Benjamin

unread,
Oct 16, 2024, 1:24:15 AM10/16/24
to Chromium Extensions, Patrick Kettner

Thank you a lot this was the answer. I can continue working on the plugin. I am thankful for your time and investigation.
Reply all
Reply to author
Forward
0 new messages