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:
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:
Any help would be greatly appreciated!
I checked your extension code, it seems to me, the problem is simple: you are under the impression that you uploaded the build files (dist
folder), but from what I see, you uploaded the whole source folder (the parent of dist
folder), the dead giveaway is the .tsx
file on the error script reference link:
chrome-extension://f…ntent/content.tsx:1
The solution is, you should upload just the dist
folder, not the parent of dist
folder.