UNPKG

extension-develop

Version:
1,347 lines (1,166 loc) 66.8 kB
"use strict"; var __webpack_require__ = {}; (()=>{ __webpack_require__.d = (exports1, definition)=>{ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { enumerable: true, get: definition[key] }); }; })(); (()=>{ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); })(); (()=>{ __webpack_require__.r = (exports1)=>{ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { value: 'Module' }); Object.defineProperty(exports1, '__esModule', { value: true }); }; })(); var __webpack_exports__ = {}; __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, { default: ()=>add_content_script_wrapper }); const external_path_namespaceObject = require("path"); const external_fs_namespaceObject = require("fs"); const external_loader_utils_namespaceObject = require("loader-utils"); const external_schema_utils_namespaceObject = require("schema-utils"); function extractCSSImports(source) { const cssImports = []; const lines = source.split('\n'); const cssImportPatterns = [ /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*;?\s*$/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*from\s+['"][^'"]*['"]/ ]; for (const line of lines){ const trimmedLine = line.trim(); if (!(trimmedLine.startsWith('//') || trimmedLine.startsWith('/*'))) for (const pattern of cssImportPatterns){ const match = pattern.exec(line); if (match) { const cssPath = match[1]; if (cssPath && !cssImports.includes(cssPath)) cssImports.push(cssPath); } } } return cssImports; } function generateReactWrapperCode(source, resourcePath) { external_path_namespaceObject.basename(resourcePath, external_path_namespaceObject.extname(resourcePath)); const cssImports = extractCSSImports(source); const resourceDir = external_path_namespaceObject.dirname(resourcePath); if ('development' === process.env.EXTENSION_ENV) console.log("[Extension.js] Detected React framework with CSS imports:", cssImports); const cssContentMap = {}; for (const cssImport of cssImports)try { const cssPath = external_path_namespaceObject.resolve(resourceDir, cssImport); if (external_fs_namespaceObject.existsSync(cssPath)) { const cssContent = external_fs_namespaceObject.readFileSync(cssPath, 'utf-8'); const escapedCSS = cssContent.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '\\${').replace(/\n/g, ' ').replace(/\s+/g, ' ').trim(); cssContentMap[cssImport] = escapedCSS; if ('development' === process.env.EXTENSION_ENV) console.log(`[Extension.js] Read CSS content for ${cssImport}, length: ${cssContent.length}`); } else if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] CSS file not found: ${cssPath}`); } catch (error) { if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] Failed to read CSS file ${cssImport}:`, error); } const wrapperCode = ` // React Content Script Wrapper - Auto-generated by Extension.js // This wrapper provides Shadow DOM isolation and CSS injection for React content scripts // Original React content script source (directive processed) ${source.replace(/'use shadow-dom'/g, "// 'use shadow-dom'").replace(/"use shadow-dom"/g, '// "use shadow-dom"')} // Content script options interface export interface ContentScriptOptions { rootElement?: string rootClassName?: string stylesheets?: string[] } // Content script instance interface export interface ContentScriptInstance { mount: (container: HTMLElement) => void unmount: () => void } // React Content script wrapper class class ReactContentScriptWrapper { private rootElement: HTMLElement | null = null private shadowRoot: ShadowRoot | null = null private styleElement: HTMLStyleElement | null = null private renderFunction: (container: HTMLElement) => (() => void) | void private unmountFunction: (() => void) | null = null private options: ContentScriptOptions constructor(renderFunction: (container: HTMLElement) => (() => void) | void, options: ContentScriptOptions = {}) { this.renderFunction = renderFunction this.options = { rootElement: 'extension-root', rootClassName: undefined, // React handles its own styling stylesheets: ${JSON.stringify(cssImports.length > 0 ? cssImports : [ './styles.css' ])}, ...options } } async mount(container?: HTMLElement): Promise<void> { console.log('[Extension.js] React wrapper mount called') if (this.rootElement) { this.unmount() } // Create root element - React handles its own container styling this.rootElement = container || document.createElement('div') this.rootElement.id = this.options.rootElement! if (this.options.rootClassName) { this.rootElement.className = this.options.rootClassName } // React component handles its own styling // Create shadow root for style isolation this.shadowRoot = this.rootElement.attachShadow({ mode: 'open' }) // Create a host element inside the shadow root for rendering const host = document.createElement('div') this.shadowRoot.appendChild(host) // Inject styles FIRST console.log('[Extension.js] About to inject styles') await this.injectStyles() // Render React content console.log('[Extension.js] About to render React content') const result = this.renderFunction(host) if (typeof result === 'function') { this.unmountFunction = result } // Append to document if no container provided if (!container) { document.body.appendChild(this.rootElement) } console.log('[Extension.js] React wrapper mount complete') } unmount() { if (this.unmountFunction) { this.unmountFunction() this.unmountFunction = null } if (this.rootElement && this.rootElement.parentNode) { this.rootElement.parentNode.removeChild(this.rootElement) } this.rootElement = null this.shadowRoot = null this.styleElement = null } // Inject styles with hardcoded CSS content for React templates private async injectStyles(): Promise<void> { console.log('[Extension.js] React injectStyles called') const targetRoot = this.shadowRoot || this.rootElement! // Create style element this.styleElement = document.createElement('style') targetRoot.appendChild(this.styleElement) // Fetch CSS content try { console.log('[Extension.js] About to fetch CSS') const cssContent = await this.fetchCSS() console.log('[Extension.js] CSS fetched, length:', cssContent.length) this.styleElement.textContent = cssContent console.log('[Extension.js] React CSS injected successfully') } catch (error) { console.error('[Extension.js] Failed to inject React CSS:', error) } // Setup HMR for CSS files this.setupCSSHMR() } // Fetch CSS with hardcoded content for React templates private async fetchCSS(): Promise<string> { let allCSS = '' console.log('[Extension.js] Processing React stylesheets:', this.options.stylesheets) // CSS content map is injected at build time const cssContentMap: Record<string, string> = ${JSON.stringify(cssContentMap)} for (const stylesheet of this.options.stylesheets) { try { console.log('[Extension.js] Processing React stylesheet:', stylesheet) // Check if we have hardcoded content for this stylesheet if (cssContentMap[stylesheet]) { const cssContent = cssContentMap[stylesheet] allCSS += cssContent + '\n' console.log('[Extension.js] Successfully injected React', stylesheet, 'content') continue } // For stylesheets without hardcoded content, try to fetch them const cssUrl = new URL(stylesheet, import.meta.url) const response = await fetch(cssUrl) const text = await response.text() if (response.ok) { allCSS += text + '\n' console.log('[Extension.js] Successfully fetched stylesheet:', stylesheet) } else { console.warn('[Extension.js] Failed to fetch CSS:', stylesheet) } } catch (error) { console.warn('[Extension.js] Failed to fetch React CSS:', stylesheet, error) } } const result = allCSS || '/* No CSS loaded */' console.log('[Extension.js] Final CSS result length:', result.length) return result } // Setup CSS HMR for React templates private setupCSSHMR() { if (!import.meta.webpackHot) return // Setup HMR for each CSS file for (const stylesheet of this.options.stylesheets) { import.meta.webpackHot?.accept(stylesheet, async () => { try { const cssContent = await this.fetchCSS() if (this.styleElement) { this.styleElement.textContent = cssContent console.log('[Extension.js] React CSS updated via HMR:', stylesheet) } } catch (error) { console.error('[Extension.js] Failed to update React CSS via HMR:', stylesheet, error) } }) } } } // Initialize React content script with wrapper function initializeReactContentScript( options: ContentScriptOptions, renderFunction: (container: HTMLElement) => (() => void) | void ): ContentScriptInstance { const wrapper = new ReactContentScriptWrapper(renderFunction, options) return { mount: (container?: HTMLElement) => wrapper.mount(container), unmount: () => wrapper.unmount() } } // Auto-initialize the React content script with the wrapper export function autoInitializeReactContentScript( options: ContentScriptOptions = {} ): ContentScriptInstance { // Get the render function from the imported contentScript const renderFunction = contentScript(options) // Initialize with the wrapper using the detected CSS imports return initializeReactContentScript(options, renderFunction) } // Simple initialization for React let unmount: (() => void) | undefined async function initialize() { console.log('[Extension.js] React wrapper initialize called') if (unmount) { console.log('[Extension.js] Unmounting previous React instance') unmount() } // Get the render function from the contentScript function const renderFunction = contentScript({}) const wrapper = new ReactContentScriptWrapper(renderFunction, {}) await wrapper.mount() unmount = () => wrapper.unmount() console.log('[Extension.js] React wrapper initialization complete') } if (import.meta.webpackHot) { import.meta.webpackHot?.accept() import.meta.webpackHot?.dispose(() => unmount?.()) // Accept changes to this file import.meta.webpackHot?.accept(() => { initialize() }) } if (document.readyState === 'complete') { initialize() } else { document.addEventListener('readystatechange', () => { if (document.readyState === 'complete') { initialize() } }) } export default ReactContentScriptWrapper `; return wrapperCode; } function vue_content_script_wrapper_extractCSSImports(source) { const cssImports = []; const lines = source.split('\n'); const cssImportPatterns = [ /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*;?\s*$/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*from\s+['"][^'"]*['"]/ ]; for (const line of lines){ const trimmedLine = line.trim(); if (!(trimmedLine.startsWith('//') || trimmedLine.startsWith('/*'))) for (const pattern of cssImportPatterns){ const match = pattern.exec(line); if (match) { const cssPath = match[1]; if (cssPath && !cssImports.includes(cssPath)) cssImports.push(cssPath); } } } return cssImports; } function generateVueWrapperCode(source, resourcePath) { external_path_namespaceObject.basename(resourcePath, external_path_namespaceObject.extname(resourcePath)); const cssImports = vue_content_script_wrapper_extractCSSImports(source); const resourceDir = external_path_namespaceObject.dirname(resourcePath); if ('development' === process.env.EXTENSION_ENV) console.log("[Extension.js] Detected Vue framework with CSS imports:", cssImports); const cssContentMap = {}; for (const cssImport of cssImports)try { const cssPath = external_path_namespaceObject.resolve(resourceDir, cssImport); if (external_fs_namespaceObject.existsSync(cssPath)) { const cssContent = external_fs_namespaceObject.readFileSync(cssPath, 'utf-8'); const escapedCSS = cssContent.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '\\${').replace(/\n/g, ' ').replace(/\s+/g, ' ').trim(); cssContentMap[cssImport] = escapedCSS; if ('development' === process.env.EXTENSION_ENV) console.log(`[Extension.js] Read CSS content for ${cssImport}, length: ${cssContent.length}`); } else if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] CSS file not found: ${cssPath}`); } catch (error) { if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] Failed to read CSS file ${cssImport}:`, error); } const wrapperCode = ` // Vue Content Script Wrapper - Auto-generated by Extension.js // This wrapper provides Shadow DOM isolation and CSS injection for Vue content scripts // Original Vue content script source (directive processed) ${source.replace(/'use shadow-dom'/g, "// 'use shadow-dom'").replace(/"use shadow-dom"/g, '// "use shadow-dom"')} // Content script options interface export interface ContentScriptOptions { rootElement?: string rootClassName?: string stylesheets?: string[] } // Content script instance interface export interface ContentScriptInstance { mount: (container: HTMLElement) => void unmount: () => void } // Vue Content script wrapper class class VueContentScriptWrapper { private rootElement: HTMLElement | null = null private shadowRoot: ShadowRoot | null = null private styleElement: HTMLStyleElement | null = null private renderFunction: (container: HTMLElement) => (() => void) | void private unmountFunction: (() => void) | null = null private options: ContentScriptOptions constructor(renderFunction: (container: HTMLElement) => (() => void) | void, options: ContentScriptOptions = {}) { this.renderFunction = renderFunction this.options = { rootElement: 'extension-root', rootClassName: undefined, // Vue handles its own styling stylesheets: ${JSON.stringify(cssImports.length > 0 ? cssImports : [ './styles.css' ])}, ...options } } async mount(container?: HTMLElement): Promise<void> { console.log('[Extension.js] Vue wrapper mount called') if (this.rootElement) { this.unmount() } // Create root element - Vue handles its own container styling this.rootElement = container || document.createElement('div') this.rootElement.id = this.options.rootElement! if (this.options.rootClassName) { this.rootElement.className = this.options.rootClassName } // Vue component handles its own styling // Create shadow root for style isolation this.shadowRoot = this.rootElement.attachShadow({ mode: 'open' }) // Create a host element inside the shadow root for rendering const host = document.createElement('div') this.shadowRoot.appendChild(host) // Inject styles FIRST console.log('[Extension.js] About to inject styles') await this.injectStyles() // Render Vue content console.log('[Extension.js] About to render Vue content') const result = this.renderFunction(host) if (typeof result === 'function') { this.unmountFunction = result } // Append to document if no container provided if (!container) { document.body.appendChild(this.rootElement) } console.log('[Extension.js] Vue wrapper mount complete') } unmount() { if (this.unmountFunction) { this.unmountFunction() this.unmountFunction = null } if (this.rootElement && this.rootElement.parentNode) { this.rootElement.parentNode.removeChild(this.rootElement) } this.rootElement = null this.shadowRoot = null this.styleElement = null } // Inject styles with hardcoded CSS content for Vue templates private async injectStyles(): Promise<void> { console.log('[Extension.js] Vue injectStyles called') const targetRoot = this.shadowRoot || this.rootElement! // Create style element this.styleElement = document.createElement('style') targetRoot.appendChild(this.styleElement) // Fetch CSS content try { console.log('[Extension.js] About to fetch CSS') const cssContent = await this.fetchCSS() console.log('[Extension.js] CSS fetched, length:', cssContent.length) this.styleElement.textContent = cssContent console.log('[Extension.js] Vue CSS injected successfully') } catch (error) { console.error('[Extension.js] Failed to inject Vue CSS:', error) } // Setup HMR for CSS files this.setupCSSHMR() } // Fetch CSS with hardcoded content for Vue templates private async fetchCSS(): Promise<string> { let allCSS = '' console.log('[Extension.js] Processing Vue stylesheets:', this.options.stylesheets) // CSS content map is injected at build time const cssContentMap: Record<string, string> = ${JSON.stringify(cssContentMap)} for (const stylesheet of this.options.stylesheets) { try { console.log('[Extension.js] Processing Vue stylesheet:', stylesheet) // Check if we have hardcoded content for this stylesheet if (cssContentMap[stylesheet]) { const cssContent = cssContentMap[stylesheet] allCSS += cssContent + '\n' console.log('[Extension.js] Successfully injected Vue', stylesheet, 'content') continue } // For stylesheets without hardcoded content, try to fetch them const cssUrl = new URL(stylesheet, import.meta.url) const response = await fetch(cssUrl) const text = await response.text() if (response.ok) { allCSS += text + '\n' console.log('[Extension.js] Successfully fetched stylesheet:', stylesheet) } else { console.warn('[Extension.js] Failed to fetch CSS:', stylesheet) } } catch (error) { console.warn('[Extension.js] Failed to fetch Vue CSS:', stylesheet, error) } } return allCSS } // Setup CSS HMR for Vue templates private setupCSSHMR() { if (!import.meta.webpackHot) return // Setup HMR for each CSS file for (const stylesheet of this.options.stylesheets) { import.meta.webpackHot?.accept(stylesheet, async () => { try { const cssContent = await this.fetchCSS() if (this.styleElement) { this.styleElement.textContent = cssContent console.log('[Extension.js] Vue CSS updated via HMR:', stylesheet) } } catch (error) { console.error('[Extension.js] Failed to update Vue CSS via HMR:', stylesheet, error) } }) } } } // Initialize Vue content script with wrapper function initializeVueContentScript( options: ContentScriptOptions, renderFunction: (container: HTMLElement) => (() => void) | void ): ContentScriptInstance { const wrapper = new VueContentScriptWrapper(renderFunction, options) return { mount: (container?: HTMLElement) => wrapper.mount(container), unmount: () => wrapper.unmount() } } // Auto-initialize the Vue content script with the wrapper export function autoInitializeVueContentScript( options: ContentScriptOptions = {} ): ContentScriptInstance { // Get the render function from the imported contentScript const renderFunction = contentScript(options) // Initialize with the wrapper using the detected CSS imports return initializeVueContentScript(options, renderFunction) } // Simple initialization for Vue let unmount: (() => void) | undefined async function initialize() { console.log('[Extension.js] Vue wrapper initialize called') if (unmount) { console.log('[Extension.js] Unmounting previous Vue instance') unmount() } // Get the render function from the contentScript function const renderFunction = contentScript({}) const wrapper = new VueContentScriptWrapper(renderFunction, {}) await wrapper.mount() unmount = () => wrapper.unmount() console.log('[Extension.js] Vue wrapper initialization complete') } if (import.meta.webpackHot) { import.meta.webpackHot?.accept() import.meta.webpackHot?.dispose(() => unmount?.()) // Accept changes to this file import.meta.webpackHot?.accept(() => { initialize() }) } if (document.readyState === 'complete') { initialize() } else { document.addEventListener('readystatechange', () => { if (document.readyState === 'complete') { initialize() } }) } export default VueContentScriptWrapper `; return wrapperCode; } function svelte_content_script_wrapper_extractCSSImports(source) { const cssImports = []; const lines = source.split('\n'); const cssImportPatterns = [ /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*;?\s*$/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*from\s+['"][^'"]*['"]/ ]; for (const line of lines){ const trimmedLine = line.trim(); if (!(trimmedLine.startsWith('//') || trimmedLine.startsWith('/*'))) for (const pattern of cssImportPatterns){ const match = pattern.exec(line); if (match) { const cssPath = match[1]; if (cssPath && !cssImports.includes(cssPath)) cssImports.push(cssPath); } } } return cssImports; } function generateSvelteWrapperCode(source, resourcePath) { external_path_namespaceObject.basename(resourcePath, external_path_namespaceObject.extname(resourcePath)); const cssImports = svelte_content_script_wrapper_extractCSSImports(source); const resourceDir = external_path_namespaceObject.dirname(resourcePath); if ('development' === process.env.EXTENSION_ENV) console.log("[Extension.js] Detected Svelte framework with CSS imports:", cssImports); const cssContentMap = {}; for (const cssImport of cssImports)try { const cssPath = external_path_namespaceObject.resolve(resourceDir, cssImport); if (external_fs_namespaceObject.existsSync(cssPath)) { const cssContent = external_fs_namespaceObject.readFileSync(cssPath, 'utf-8'); const escapedCSS = cssContent.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '\\${').replace(/\n/g, ' ').replace(/\s+/g, ' ').trim(); cssContentMap[cssImport] = escapedCSS; if ('development' === process.env.EXTENSION_ENV) console.log(`[Extension.js] Read CSS content for ${cssImport}, length: ${cssContent.length}`); } else if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] CSS file not found: ${cssPath}`); } catch (error) { if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] Failed to read CSS file ${cssImport}:`, error); } const wrapperCode = ` // Svelte Content Script Wrapper - Auto-generated by Extension.js // This wrapper provides Shadow DOM isolation and CSS injection for Svelte content scripts // Original Svelte content script source (directive processed) ${source.replace(/'use shadow-dom'/g, "// 'use shadow-dom'").replace(/"use shadow-dom"/g, '// "use shadow-dom"')} // Content script options interface export interface ContentScriptOptions { rootElement?: string rootClassName?: string stylesheets?: string[] } // Content script instance interface export interface ContentScriptInstance { mount: (container: HTMLElement) => void unmount: () => void } // Svelte Content script wrapper class class SvelteContentScriptWrapper { private rootElement: HTMLElement | null = null private shadowRoot: ShadowRoot | null = null private styleElement: HTMLStyleElement | null = null private renderFunction: (container: HTMLElement) => (() => void) | void private unmountFunction: (() => void) | null = null private options: ContentScriptOptions constructor(renderFunction: (container: HTMLElement) => (() => void) | void, options: ContentScriptOptions = {}) { this.renderFunction = renderFunction this.options = { rootElement: 'extension-root', rootClassName: undefined, // Svelte handles its own styling stylesheets: ${JSON.stringify(cssImports.length > 0 ? cssImports : [ './styles.css' ])}, ...options } } async mount(container?: HTMLElement): Promise<void> { console.log('[Extension.js] Svelte wrapper mount called') if (this.rootElement) { this.unmount() } // Create root element - Svelte handles its own container styling this.rootElement = container || document.createElement('div') this.rootElement.id = this.options.rootElement! if (this.options.rootClassName) { this.rootElement.className = this.options.rootClassName } // Svelte component handles its own styling // Create shadow root for style isolation this.shadowRoot = this.rootElement.attachShadow({ mode: 'open' }) // Create a host element inside the shadow root for rendering const host = document.createElement('div') this.shadowRoot.appendChild(host) // Inject styles FIRST console.log('[Extension.js] About to inject styles') await this.injectStyles() // Render Svelte content console.log('[Extension.js] About to render Svelte content') const result = this.renderFunction(host) if (typeof result === 'function') { this.unmountFunction = result } // Append to document if no container provided if (!container) { document.body.appendChild(this.rootElement) } console.log('[Extension.js] Svelte wrapper mount complete') } unmount() { if (this.unmountFunction) { this.unmountFunction() this.unmountFunction = null } if (this.rootElement && this.rootElement.parentNode) { this.rootElement.parentNode.removeChild(this.rootElement) } this.rootElement = null this.shadowRoot = null this.styleElement = null } // Inject styles with hardcoded CSS content for Svelte templates private async injectStyles(): Promise<void> { console.log('[Extension.js] Svelte injectStyles called') const targetRoot = this.shadowRoot || this.rootElement! // Create style element this.styleElement = document.createElement('style') targetRoot.appendChild(this.styleElement) // Fetch CSS content try { console.log('[Extension.js] About to fetch CSS') const cssContent = await this.fetchCSS() console.log('[Extension.js] CSS fetched, length:', cssContent.length) this.styleElement.textContent = cssContent console.log('[Extension.js] Svelte CSS injected successfully') } catch (error) { console.error('[Extension.js] Failed to inject Svelte CSS:', error) } // Setup HMR for CSS files this.setupCSSHMR() } // Fetch CSS with hardcoded content for Svelte templates private async fetchCSS(): Promise<string> { let allCSS = '' console.log('[Extension.js] Processing Svelte stylesheets:', this.options.stylesheets) // CSS content map is injected at build time const cssContentMap: Record<string, string> = ${JSON.stringify(cssContentMap)} for (const stylesheet of this.options.stylesheets) { try { console.log('[Extension.js] Processing Svelte stylesheet:', stylesheet) // Check if we have hardcoded content for this stylesheet if (cssContentMap[stylesheet]) { const cssContent = cssContentMap[stylesheet] allCSS += cssContent + '\n' console.log('[Extension.js] Successfully injected Svelte', stylesheet, 'content') continue } // For stylesheets without hardcoded content, try to fetch them const cssUrl = new URL(stylesheet, import.meta.url) const response = await fetch(cssUrl) const text = await response.text() if (response.ok) { allCSS += text + '\n' console.log('[Extension.js] Successfully fetched stylesheet:', stylesheet) } else { console.warn('[Extension.js] Failed to fetch CSS:', stylesheet) } } catch (error) { console.warn('[Extension.js] Failed to fetch Svelte CSS:', stylesheet, error) } } return allCSS } // Setup CSS HMR for Svelte templates private setupCSSHMR() { if (!import.meta.webpackHot) return // Setup HMR for each CSS file for (const stylesheet of this.options.stylesheets) { import.meta.webpackHot?.accept(stylesheet, async () => { try { const cssContent = await this.fetchCSS() if (this.styleElement) { this.styleElement.textContent = cssContent console.log('[Extension.js] Svelte CSS updated via HMR:', stylesheet) } } catch (error) { console.error('[Extension.js] Failed to update Svelte CSS via HMR:', stylesheet, error) } }) } } } // Initialize Svelte content script with wrapper function initializeSvelteContentScript( options: ContentScriptOptions, renderFunction: (container: HTMLElement) => (() => void) | void ): ContentScriptInstance { const wrapper = new SvelteContentScriptWrapper(renderFunction, options) return { mount: (container?: HTMLElement) => wrapper.mount(container), unmount: () => wrapper.unmount() } } // Auto-initialize the Svelte content script with the wrapper export function autoInitializeSvelteContentScript( options: ContentScriptOptions = {} ): ContentScriptInstance { // Get the render function from the imported contentScript const renderFunction = contentScript(options) // Initialize with the wrapper using the detected CSS imports return initializeSvelteContentScript(options, renderFunction) } // Simple initialization for Svelte let unmount: (() => void) | undefined async function initialize() { console.log('[Extension.js] Svelte wrapper initialize called') if (unmount) { console.log('[Extension.js] Unmounting previous Svelte instance') unmount() } // Get the render function from the contentScript function const renderFunction = contentScript({}) const wrapper = new SvelteContentScriptWrapper(renderFunction, {}) await wrapper.mount() unmount = () => wrapper.unmount() console.log('[Extension.js] Svelte wrapper initialization complete') } if (import.meta.webpackHot) { import.meta.webpackHot?.accept() import.meta.webpackHot?.dispose(() => unmount?.()) // Accept changes to this file import.meta.webpackHot?.accept(() => { initialize() }) } if (document.readyState === 'complete') { initialize() } else { document.addEventListener('readystatechange', () => { if (document.readyState === 'complete') { initialize() } }) } export default SvelteContentScriptWrapper `; return wrapperCode; } function preact_content_script_wrapper_extractCSSImports(source) { const cssImports = []; const lines = source.split('\n'); const cssImportPatterns = [ /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*;?\s*$/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*from\s+['"][^'"]*['"]/ ]; for (const line of lines){ const trimmedLine = line.trim(); if (!(trimmedLine.startsWith('//') || trimmedLine.startsWith('/*'))) for (const pattern of cssImportPatterns){ const match = pattern.exec(line); if (match) { const cssPath = match[1]; if (cssPath && !cssImports.includes(cssPath)) cssImports.push(cssPath); } } } return cssImports; } function generatePreactWrapperCode(source, resourcePath) { external_path_namespaceObject.basename(resourcePath, external_path_namespaceObject.extname(resourcePath)); const cssImports = preact_content_script_wrapper_extractCSSImports(source); const resourceDir = external_path_namespaceObject.dirname(resourcePath); if ('development' === process.env.EXTENSION_ENV) console.log("[Extension.js] Detected Preact framework with CSS imports:", cssImports); const cssContentMap = {}; for (const cssImport of cssImports)try { const cssPath = external_path_namespaceObject.resolve(resourceDir, cssImport); if (external_fs_namespaceObject.existsSync(cssPath)) { const cssContent = external_fs_namespaceObject.readFileSync(cssPath, 'utf-8'); const escapedCSS = cssContent.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '\\${').replace(/\n/g, ' ').replace(/\s+/g, ' ').trim(); cssContentMap[cssImport] = escapedCSS; if ('development' === process.env.EXTENSION_ENV) console.log(`[Extension.js] Read CSS content for ${cssImport}, length: ${cssContent.length}`); } else if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] CSS file not found: ${cssPath}`); } catch (error) { if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] Failed to read CSS file ${cssImport}:`, error); } const wrapperCode = ` // Preact Content Script Wrapper - Auto-generated by Extension.js // This wrapper provides Shadow DOM isolation and CSS injection for Preact content scripts // Original Preact content script source (directive processed) ${source.replace(/'use shadow-dom'/g, "// 'use shadow-dom'").replace(/"use shadow-dom"/g, '// "use shadow-dom"')} // Content script options interface export interface ContentScriptOptions { rootElement?: string rootClassName?: string stylesheets?: string[] } // Content script instance interface export interface ContentScriptInstance { mount: (container: HTMLElement) => void unmount: () => void } // Preact Content script wrapper class class PreactContentScriptWrapper { private rootElement: HTMLElement | null = null private shadowRoot: ShadowRoot | null = null private styleElement: HTMLStyleElement | null = null private renderFunction: (container: HTMLElement) => (() => void) | void private unmountFunction: (() => void) | null = null private options: ContentScriptOptions constructor(renderFunction: (container: HTMLElement) => (() => void) | void, options: ContentScriptOptions = {}) { this.renderFunction = renderFunction this.options = { rootElement: 'extension-root', rootClassName: undefined, // Preact handles its own styling stylesheets: ${JSON.stringify(cssImports.length > 0 ? cssImports : [ './styles.css' ])}, ...options } } async mount(container?: HTMLElement): Promise<void> { console.log('[Extension.js] Preact wrapper mount called') if (this.rootElement) { this.unmount() } // Create root element - Preact handles its own container styling this.rootElement = container || document.createElement('div') this.rootElement.id = this.options.rootElement! if (this.options.rootClassName) { this.rootElement.className = this.options.rootClassName } // Preact component handles its own styling // Create shadow root for style isolation this.shadowRoot = this.rootElement.attachShadow({ mode: 'open' }) // Inject styles FIRST console.log('[Extension.js] About to inject styles') await this.injectStyles() // Render Preact content console.log('[Extension.js] About to render Preact content') const result = this.renderFunction(this.shadowRoot as any) if (typeof result === 'function') { this.unmountFunction = result } // Append to document if no container provided if (!container) { document.body.appendChild(this.rootElement) } console.log('[Extension.js] Preact wrapper mount complete') } unmount() { if (this.unmountFunction) { this.unmountFunction() this.unmountFunction = null } if (this.rootElement && this.rootElement.parentNode) { this.rootElement.parentNode.removeChild(this.rootElement) } this.rootElement = null this.shadowRoot = null this.styleElement = null } // Inject styles with hardcoded CSS content for Preact templates private async injectStyles(): Promise<void> { console.log('[Extension.js] Preact injectStyles called') const targetRoot = this.shadowRoot || this.rootElement! // Create style element this.styleElement = document.createElement('style') targetRoot.appendChild(this.styleElement) // Fetch CSS content try { console.log('[Extension.js] About to fetch CSS') const cssContent = await this.fetchCSS() console.log('[Extension.js] CSS fetched, length:', cssContent.length) this.styleElement.textContent = cssContent console.log('[Extension.js] Preact CSS injected successfully') } catch (error) { console.error('[Extension.js] Failed to inject Preact CSS:', error) } // Setup HMR for CSS files this.setupCSSHMR() } // Fetch CSS with hardcoded content for Preact templates private async fetchCSS(): Promise<string> { let allCSS = '' console.log('[Extension.js] Processing Preact stylesheets:', this.options.stylesheets) // CSS content map is injected at build time const cssContentMap: Record<string, string> = ${JSON.stringify(cssContentMap)} for (const stylesheet of this.options.stylesheets) { try { console.log('[Extension.js] Processing Preact stylesheet:', stylesheet) // Check if we have hardcoded content for this stylesheet if (cssContentMap[stylesheet]) { const cssContent = cssContentMap[stylesheet] allCSS += cssContent + '\\n' console.log(\`[Extension.js] Successfully injected Preact \${stylesheet} content\`) continue } // For stylesheets without hardcoded content, try to fetch them const cssUrl = new URL(stylesheet, import.meta.url) const response = await fetch(cssUrl) const text = await response.text() if (response.ok) { allCSS += text + '\\n' console.log('[Extension.js] Successfully fetched stylesheet:', stylesheet) } else { console.warn('[Extension.js] Failed to fetch CSS:', stylesheet) } } catch (error) { console.warn('[Extension.js] Failed to fetch Preact CSS:', stylesheet, error) } } return allCSS } // Setup CSS HMR for Preact templates private setupCSSHMR() { if (!import.meta.webpackHot) return // Setup HMR for each CSS file for (const stylesheet of this.options.stylesheets) { import.meta.webpackHot?.accept(stylesheet, async () => { try { const cssContent = await this.fetchCSS() if (this.styleElement) { this.styleElement.textContent = cssContent console.log('[Extension.js] Preact CSS updated via HMR:', stylesheet) } } catch (error) { console.error('[Extension.js] Failed to update Preact CSS via HMR:', stylesheet, error) } }) } } } // Initialize Preact content script with wrapper function initializePreactContentScript( options: ContentScriptOptions, renderFunction: (container: HTMLElement) => (() => void) | void ): ContentScriptInstance { const wrapper = new PreactContentScriptWrapper(renderFunction, options) return { mount: (container?: HTMLElement) => wrapper.mount(container), unmount: () => wrapper.unmount() } } // Auto-initialize the Preact content script with the wrapper export function autoInitializePreactContentScript( options: ContentScriptOptions = {} ): ContentScriptInstance { // Get the render function from the imported contentScript const renderFunction = contentScript(options) // Initialize with the wrapper using the detected CSS imports return initializePreactContentScript(options, renderFunction) } // Simple initialization for Preact let unmount: (() => void) | undefined async function initialize() { console.log('[Extension.js] Preact wrapper initialize called') if (unmount) { console.log('[Extension.js] Unmounting previous Preact instance') unmount() } // Get the render function from the contentScript function const renderFunction = contentScript({}) const wrapper = new PreactContentScriptWrapper(renderFunction, {}) await wrapper.mount() unmount = () => wrapper.unmount() console.log('[Extension.js] Preact wrapper initialization complete') } if (import.meta.webpackHot) { import.meta.webpackHot?.accept() import.meta.webpackHot?.dispose(() => unmount?.()) // Accept changes to this file import.meta.webpackHot?.accept(() => { initialize() }) } if (document.readyState === 'complete') { initialize() } else { document.addEventListener('readystatechange', () => { if (document.readyState === 'complete') { initialize() } }) } export default PreactContentScriptWrapper `; return wrapperCode; } function typescript_content_script_wrapper_extractCSSImports(source) { const cssImports = []; const lines = source.split('\n'); const cssImportPatterns = [ /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*;?\s*$/, /^\s*import\s+['"]([^'"]*\.(?:css|scss|sass|less|module\.css))['"]\s*from\s+['"][^'"]*['"]/ ]; for (const line of lines){ const trimmedLine = line.trim(); if (!(trimmedLine.startsWith('//') || trimmedLine.startsWith('/*'))) for (const pattern of cssImportPatterns){ const match = pattern.exec(line); if (match) { const cssPath = match[1]; if (cssPath && !cssImports.includes(cssPath)) cssImports.push(cssPath); } } } return cssImports; } function generateTypeScriptWrapperCode(source, resourcePath) { external_path_namespaceObject.basename(resourcePath, external_path_namespaceObject.extname(resourcePath)); const cssImports = typescript_content_script_wrapper_extractCSSImports(source); const resourceDir = external_path_namespaceObject.dirname(resourcePath); if ('development' === process.env.EXTENSION_ENV) console.log("[Extension.js] Detected TypeScript framework with CSS imports:", cssImports); const cssContentMap = {}; for (const cssImport of cssImports)try { const cssPath = external_path_namespaceObject.resolve(resourceDir, cssImport); if (external_fs_namespaceObject.existsSync(cssPath)) { const cssContent = external_fs_namespaceObject.readFileSync(cssPath, 'utf-8'); const escapedCSS = cssContent.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '\\${').replace(/\n/g, ' ').replace(/\s+/g, ' ').trim(); cssContentMap[cssImport] = escapedCSS; if ('development' === process.env.EXTENSION_ENV) console.log(`[Extension.js] Read CSS content for ${cssImport}, length: ${cssContent.length}`); } else if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] CSS file not found: ${cssPath}`); } catch (error) { if ('development' === process.env.EXTENSION_ENV) console.warn(`[Extension.js] Failed to read CSS file ${cssImport}:`, error); } const wrapperCode = ` // TypeScript Content Script Wrapper - Auto-generated by Extension.js // This wrapper provides Shadow DOM isolation and CSS injection for TypeScript content scripts // Original TypeScript content script source (directive processed) ${source.replace(/'use shadow-dom'/g, "// 'use shadow-dom'").replace(/"use shadow-dom"/g, '// "use shadow-dom"')} // Content script options interface export interface ContentScriptOptions { rootElement?: string rootClassName?: string stylesheets?: string[] } // Content script instance interface export interface ContentScriptInstance { mount: (container: HTMLElement) => void unmount: () => void } // TypeScript Content script wrapper class class TypeScriptContentScriptWrapper { private rootElement: HTMLElement | null = null private shadowRoot: ShadowRoot | null = null private styleElement: HTMLStyleElement | null = null private renderFunction: (container: HTMLElement) => (() => void) | void private unmountFunction: (() => void) | null = null private options: ContentScriptOptions constructor(renderFunction: (container: HTMLElement) => (() => void) | void, options: ContentScriptOptions = {}) { this.renderFunction = renderFunction this.options = { rootElement: 'extension-root', rootClassName: undefined, // TypeScript handles its own styling stylesheets: ${JSON.stringify(cssImports)}, ...options } } async mount(container?: HTMLElement): Promise<void> { console.log('[Extension.js] TypeScript wrapper mount called') if (this.rootElement) { this.unmount() } // Create root element - TypeScript handles its own container styling this.rootElement = container || document.createElement('div') this.rootElement.id = this.options.rootElement! if (this.options.rootClassName) { this.rootElement.className = this.options.rootClassName } // Create shadow root for style isolation this.shadowRoot = this.rootElement.attachShadow({ mode: 'open' }) // Inject styles FIRST console.log('[Extension.js] About to inject styles') await this.injectStyles() // Render TypeScript content console.log('[Extension.js] About to render TypeScript content') const result = this.renderFunction(this.shadowRoot as any) if (typeof result === 'function') { this.unmountFunction = result } // Append to document if no container provided if (!container) { document.body.appendChild(this.rootElement) } console.log('[Extension.js] TypeScript wrapper mount complete') } unmount() { if (this.unmountFunction) { this.unmountFunction() this.unmountFunction = null } if (this.rootElement && this.rootElement.parentNode) { this.rootElement.parentNode.removeChild(this.rootElement) } this.rootElement = null this.shadowRoot = null this.styleElement = null } // Inject styles with hardcoded CSS content for TypeScript templates private async injectStyles(): Promise<void> { console.log('[Extension.js] TypeScript injectStyles called') const targetRoot = this.shadowRoot || this.rootElement! // Create style element this.styleElement = document.createElement('style') targetRoot.appendChild(this.styleElement) // Fetch CSS content try { console.log('[Extension.js] About to fetch CSS') const cssContent = await this.fetchCSS() console.log('[Extension.js] CSS fetched, length:', cssContent.length) this.styleElement.textContent = cssContent console.log('[Extension.js] TypeScript CSS injected successfully') } catch (error) { console.error('[Extension.js] Failed to inject TypeScript CSS:', error) } // Setup HMR for CSS files this.setupCSSHMR() } // Fetch CSS with hardcoded content for TypeScript templates private async fetchCSS(): Promise<string> { let allCSS = '' console.log('[Extension.js] Processing TypeScript stylesheets:', this.options.stylesheets) // CSS content map is injected at build time const cssContentMap: Record<string, string> = ${JSON.stringify(cssContentMap)} for (const stylesheet of this.options.stylesheets) { try { console.log('[Extension.js] Processing TypeScript stylesheet:', stylesheet) // Check if we have hardcoded content for this stylesheet if (cssContentMap[stylesheet]) { const cssContent = cssContentMap[stylesheet] allCSS += cssContent + '\\n' console.log(\`[Extension.js] Successfully injected TypeScript \${stylesheet} content\`) continue } // For stylesheets without hardcoded content, try to fetch them const cssUrl = new URL(stylesheet, import.meta.url) const response = await fetch(cssUrl) const text = await response.text() if (response.ok) { allCSS += text + '\\n' console.log('[Extension.js] Successfully fetched stylesheet:', stylesheet) } else { console.warn('[Extension.js] Failed to fetch CSS:', stylesheet) } } catch (error) { console.warn('[Extension.js] Failed to fetch TypeScript CSS:', stylesheet, error) } } return allCSS } // Setup CSS HMR for TypeScript templ