Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update all templates to prepare for v2.0.0-rc.0 #256

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ab3bbfd
Avoid testing content_scripts for now
cezaraugusto Mar 18, 2025
a62cf2d
Do not clean output during `preview`
cezaraugusto Mar 19, 2025
115bd0a
Fix content_scripts CSS not loading on production
cezaraugusto Mar 21, 2025
7b4909c
Fix regression on compiler stats output
cezaraugusto Mar 19, 2025
99fa20f
Fix build
cezaraugusto Mar 21, 2025
b0ae831
Fix conflict between content_scrits and non-content_scripts CSS files
cezaraugusto Mar 24, 2025
7e91007
Create templates-v2
cezaraugusto Mar 24, 2025
aff1f3d
Add Svelte
cezaraugusto Mar 24, 2025
b674a34
Add missing mode type
cezaraugusto Mar 24, 2025
ded191f
Fx examples spec test location
cezaraugusto Mar 24, 2025
2dad262
Increase fixtures timeout for the shadow root
cezaraugusto Mar 24, 2025
f33db58
Fix build tests
cezaraugusto Mar 25, 2025
e58e8fd
Whitelist all tests
cezaraugusto Mar 25, 2025
f6c706d
Fix types
cezaraugusto Mar 25, 2025
91fdbf4
Update React templates
cezaraugusto Mar 25, 2025
7424369
Update Svelte template
cezaraugusto Mar 25, 2025
3b75329
Update content-vue template
cezaraugusto Mar 25, 2025
ace6f7b
Add Vue template
cezaraugusto Mar 25, 2025
6f4f3e2
Update testDir for e2e tests
cezaraugusto Mar 26, 2025
08d52da
Add Preact template
cezaraugusto Mar 26, 2025
ddae517
Add TypeScript template
cezaraugusto Mar 26, 2025
13fd1a4
Add JavaScript template
cezaraugusto Mar 26, 2025
3978aa6
Remove Svelte template leftovers
cezaraugusto Mar 27, 2025
5dd044b
Add webpackHot check for React templates
cezaraugusto Mar 27, 2025
2a2a137
Fix CSS-like modules not working after Rspack migration
cezaraugusto Mar 27, 2025
b6dfae0
Fix hanging SASS template runs
cezaraugusto Mar 27, 2025
bf9cb56
Remove outdated PostCSS code
cezaraugusto Mar 27, 2025
eea980c
Attempt to fix React templates e2e tests
cezaraugusto Mar 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ __TEST__
/playwright/.cache/
/e2e-report/
_examples
specs/
specs/
62 changes: 62 additions & 0 deletions examples-v2/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {type Template} from './types'

const ALL_TEMPLATES: Template[] = [
{
name: 'javascript',
uiContext: ['content'],
uiFramework: undefined,
css: 'css',
hasBackground: false,
hasEnv: false,
configFiles: undefined
},
{
name: 'typescript',
uiContext: ['content'],
uiFramework: undefined,
css: 'css',
hasBackground: false,
hasEnv: false,
configFiles: ['tsconfig.json']
},
{
name: 'react',
uiContext: ['content'],
uiFramework: 'react',
css: 'css',
hasBackground: false,
hasEnv: false,
configFiles: ['postcss.config.js', 'tailwind.config.js', 'tsconfig.json']
},
{
name: 'preact',
uiContext: ['content'],
uiFramework: 'preact',
css: 'css',
hasBackground: false,
hasEnv: false,
configFiles: ['postcss.config.js', 'tailwind.config.js', 'tsconfig.json']
},
{
name: 'vue',
uiContext: ['content'],
uiFramework: 'vue',
css: 'css',
hasBackground: false,
hasEnv: false,
configFiles: ['postcss.config.js', 'tailwind.config.js', 'tsconfig.json']
},
{
name: 'svelte',
uiContext: ['content'],
uiFramework: 'svelte',
css: 'css',
hasBackground: false,
hasEnv: false,
configFiles: ['postcss.config.js', 'tailwind.config.js', 'tsconfig.json']
}
]

const SUPPORTED_BROWSERS: string[] = ['chrome', 'edge', 'firefox']

export {SUPPORTED_BROWSERS, ALL_TEMPLATES}
104 changes: 104 additions & 0 deletions examples-v2/extension-fixtures.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
test as base,
chromium,
} from '@playwright/test'

/**
* @typedef {import('@playwright/test').BrowserContext} BrowserContext
*/
const extensionFixtures = (
/** @type {string} */ pathToExtension,
/** @type {boolean} */ headless
/** @returns {import('@playwright/test').TestModifier<{}, {context: BrowserContext, extensionId: string}>} */
) => {
return base.extend({
/** @type {() => Promise<BrowserContext>} */
// eslint-disable-next-line no-empty-pattern
context: async ({}, use) => {
const context = await chromium.launchPersistentContext('', {
headless: false,
args: [
headless ? `--headless=new` : '',
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`,
'--no-first-run', // Disable Chrome's native first run experience.
'--disable-client-side-phishing-detection', // Disables client-side phishing detection
'--disable-component-extensions-with-background-pages', // Disable some built-in extensions that aren't affected by '--disable-extensions'
'--disable-default-apps', // Disable installation of default apps
'--disable-features=InterestFeedContentSuggestions', // Disables the Discover feed on NTP
'--disable-features=Translate', // Disables Chrome translation, both the manual option and the popup prompt when a page with differing language is detected.
'--hide-scrollbars', // Hide scrollbars from screenshots.
'--mute-audio', // Mute any audio
'--no-default-browser-check', // Disable the default browser check, do not prompt to set it as such
'--no-first-run', // Skip first run wizards
'--ash-no-nudges', // Avoids blue bubble "user education" nudges (eg., "… give your browser a new look", Memory Saver)
'--disable-search-engine-choice-screen', // Disable the 2023+ search engine choice screen
'--disable-features=MediaRoute', // Avoid the startup dialog for `Do you want the application “Chromium.app” to accept incoming network connections?`. Also disables the Chrome Media Router which creates background networking activity to discover cast targets. A superset of disabling DialMediaRouteProvider.
'--use-mock-keychain', // Use mock keychain on Mac to prevent the blocking permissions dialog about "Chrome wants to use your confidential information stored in your keychain"
'--disable-background-networking', // Disable various background network services, including extension updating, safe browsing service, upgrade detector, translate, UMA
'--disable-breakpad', // Disable crashdump collection (reporting is already disabled in Chromium)
'--disable-component-update', // Don't update the browser 'components' listed at chrome://components/
'--disable-domain-reliability', // Disables Domain Reliability Monitoring, which tracks whether the browser has difficulty contacting Google-owned sites and uploads reports to Google.
'--disable-features=AutofillServerCommunicatio', // Disables autofill server communication. This feature isn't disabled via other 'parent' flags.
'--disable-features=CertificateTransparencyComponentUpdate',
'--disable-sync', // Disable syncing to a Google account
'--disable-features=OptimizationHints', // Used for turning on Breakpad crash reporting in a debug environment where crash reporting is typically compiled but disabled. Disable the Chrome Optimization Guide and networking with its service API
'--disable-features=DialMediaRouteProvider', // A weaker form of disabling the MediaRouter feature. See that flag's details.
'--no-pings', // Don't send hyperlink auditing pings
'--enable-features=SidePanelUpdates' // Ensure the side panel is visible. This is used for testing the side panel feature.
].filter((arg) => !!arg)
})
await use(context)
await context.close()
},
/** @type {() => Promise<string>} */
extensionId: async ({context}, use) => {
/*
// for manifest v2:
let [background] = context.backgroundPages()
if (!background)
background = await context.waitForEvent('backgroundpage')
*/

// for manifest v3:
let [background] = context.serviceWorkers()
if (!background) background = await context.waitForEvent('serviceworker')

const extensionId = background.url().split('/')[2]
await use(extensionId)
}
})
}

// Screenshot function
async function takeScreenshot(page, screenshotPath) {
await page.screenshot({path: screenshotPath})
}

export {extensionFixtures, takeScreenshot}

/**
* Utility to access elements inside the Shadow DOM.
* @param page The Playwright Page object.
* @param shadowHostSelector The selector for the Shadow DOM host element.
* @param innerSelector The selector for the element inside the Shadow DOM.
* @returns A Promise resolving to an ElementHandle for the inner element or null if not found.
*/
export async function getShadowRootElement(
page,
shadowHostSelector,
innerSelector
) {
const shadowHost = page.locator(shadowHostSelector)
const shadowRootHandle = await shadowHost.evaluateHandle(
(host) => host.shadowRoot
)

const innerElement = await shadowRootHandle.evaluateHandle(
(shadowRoot, selector) =>
shadowRoot.querySelector(selector),
innerSelector
)

return innerElement.asElement()
}
110 changes: 110 additions & 0 deletions examples-v2/extension-fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import {
test as base,
chromium,
type Page,
type BrowserContext,
type ElementHandle
} from '@playwright/test'

export const extensionFixtures = (
pathToExtension: string,
headless: boolean
) => {
return base.extend<{
context: BrowserContext
extensionId: string
}>({
context: async ({}, use) => {
const context = await chromium.launchPersistentContext('', {
headless: false,
args: [
headless ? `--headless=new` : '',
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`,
'--no-first-run', // Disable Chrome's native first run experience.
'--disable-client-side-phishing-detection', // Disables client-side phishing detection
'--disable-component-extensions-with-background-pages', // Disable some built-in extensions that aren't affected by '--disable-extensions'
'--disable-default-apps', // Disable installation of default apps
'--disable-features=InterestFeedContentSuggestions', // Disables the Discover feed on NTP
'--disable-features=Translate', // Disables Chrome translation, both the manual option and the popup prompt when a page with differing language is detected.
'--hide-scrollbars', // Hide scrollbars from screenshots.
'--mute-audio', // Mute any audio
'--no-default-browser-check', // Disable the default browser check, do not prompt to set it as such
'--no-first-run', // Skip first run wizards
'--ash-no-nudges', // Avoids blue bubble "user education" nudges (eg., "… give your browser a new look", Memory Saver)
'--disable-search-engine-choice-screen', // Disable the 2023+ search engine choice screen
'--disable-features=MediaRoute', // Avoid the startup dialog for `Do you want the application “Chromium.app” to accept incoming network connections?`. Also disables the Chrome Media Router which creates background networking activity to discover cast targets. A superset of disabling DialMediaRouteProvider.
'--use-mock-keychain', // Use mock keychain on Mac to prevent the blocking permissions dialog about "Chrome wants to use your confidential information stored in your keychain"
'--disable-background-networking', // Disable various background network services, including extension updating, safe browsing service, upgrade detector, translate, UMA
'--disable-breakpad', // Disable crashdump collection (reporting is already disabled in Chromium)
'--disable-component-update', // Don't update the browser 'components' listed at chrome://components/
'--disable-domain-reliability', // Disables Domain Reliability Monitoring, which tracks whether the browser has difficulty contacting Google-owned sites and uploads reports to Google.
'--disable-features=AutofillServerCommunicatio', // Disables autofill server communication. This feature isn't disabled via other 'parent' flags.
'--disable-features=CertificateTransparencyComponentUpdate',
'--disable-sync', // Disable syncing to a Google account
'--disable-features=OptimizationHints', // Used for turning on Breakpad crash reporting in a debug environment where crash reporting is typically compiled but disabled. Disable the Chrome Optimization Guide and networking with its service API
'--disable-features=DialMediaRouteProvider', // A weaker form of disabling the MediaRouter feature. See that flag's details.
'--no-pings', // Don't send hyperlink auditing pings
'--enable-features=SidePanelUpdates' // Ensure the side panel is visible. This is used for testing the side panel feature.
].filter((arg) => !!arg)
})
await use(context)
await context.close()
},
extensionId: async ({context}, use) => {
/*
// for manifest v2:
let [background] = context.backgroundPages()
if (!background)
background = await context.waitForEvent('backgroundpage')
*/

// for manifest v3:
let [background] = context.serviceWorkers()
if (!background) background = await context.waitForEvent('serviceworker')

const extensionId = background.url().split('/')[2]
await use(extensionId)
}
})
}

// Screenshot function
export async function takeScreenshot(page: any, screenshotPath: string) {
await page.screenshot({path: screenshotPath})
}

/**
* Utility to access elements inside the Shadow DOM.
* @param page The Playwright Page object.
* @param shadowHostSelector The selector for the Shadow DOM host element.
* @param innerSelector The selector for the element inside the Shadow DOM.
* @returns A Promise resolving to an ElementHandle for the inner element or null if not found.
*/
export async function getShadowRootElement(
page: Page,
shadowHostSelector: string,
innerSelector: string
): Promise<ElementHandle<HTMLElement> | null> {
// Wait for shadow host to be present first
await page.waitForSelector(shadowHostSelector, {timeout: 15000})

// Get the shadow host element
const shadowHost = await page.$(shadowHostSelector)
if (!shadowHost) return null

// Get its shadow root
const shadowRoot = await page.evaluateHandle(
(host) => host.shadowRoot,
shadowHost
)
if (!shadowRoot) return null

// Find element within shadow root
const element = await page.evaluateHandle(
(root) => root?.querySelector(innerSelector),
shadowRoot
)

return element.asElement() as ElementHandle<HTMLElement> | null
}
1 change: 1 addition & 0 deletions examples-v2/javascript/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('Hello from the background script!')
44 changes: 44 additions & 0 deletions examples-v2/javascript/content/scripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import './styles.css?inline_style'
import logo from '../images/logo.svg'

console.log('hello from content_scripts')

if (document.readyState === 'complete') {
initial()
} else {
document.addEventListener('readystatechange', () => {
if (document.readyState === 'complete') initial()
})
}

function initial() {
const rootDiv = document.createElement('div')
rootDiv.id = 'extension-root'
document.body.appendChild(rootDiv)

// Injecting content_scripts inside a shadow dom
// prevents conflicts with the host page's styles.
// This way, styles from the extension won't leak into the host page.
const shadowRoot = rootDiv.attachShadow({mode: 'open'})

// Inform Extension.js that the shadow root is available.
window.__EXTENSION_SHADOW_ROOT__ = shadowRoot

shadowRoot.innerHTML = `
<div class="content_script">
<img class="content_logo" src="${logo}" />
<h1 class="content_title">
Welcome to your Content Script Extension
</h1>
<p class="content_description">
Learn more about creating cross-browser extensions at <a
className="underline hover:no-underline"
href="https://extension.js.org"
target="_blank"
>
https://extension.js.org
</a>
</p>
</div>
`
}
40 changes: 40 additions & 0 deletions examples-v2/javascript/content/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.content_script {
color: #c9c9c9;
background-color: #0a0c10;
position: fixed;
right: 0;
bottom: 0;
z-index: 9;
width: 315px;
margin: 1rem;
padding: 2rem 1rem;
display: flex;
flex-direction: column;
gap: 1em;
border-radius: 6px;
}

.content_logo {
width: 72px;
}

.content_title {
font-size: 1.85em;
line-height: 1.1;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
font-weight: 700;
margin: 0;
}

.content_description {
font-size: small;
margin: 0;
}

.content_description a {
text-decoration: none;
border-bottom: 2px solid #c9c9c9;
color: #e5e7eb;
margin: 0;
}
Binary file added examples-v2/javascript/images/extension_48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading