migration to vite/playwright
This commit is contained in:
83
scripts/build-extensions.mjs
Normal file
83
scripts/build-extensions.mjs
Normal file
@@ -0,0 +1,83 @@
|
||||
import { readdir, stat } from 'node:fs/promises'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { build } from 'vite'
|
||||
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars'
|
||||
import string from 'vite-plugin-string'
|
||||
|
||||
const root = process.cwd()
|
||||
const extensionsRoot = resolve(root, 'src/editor/extensions')
|
||||
const outDir = resolve(root, 'dist/editor')
|
||||
|
||||
const htmlStringPlugin = string({
|
||||
include: [
|
||||
'src/editor/dialogs/**/*.html',
|
||||
'src/editor/panels/*.html',
|
||||
'src/editor/templates/*.html',
|
||||
'src/editor/extensions/*/*.html'
|
||||
]
|
||||
})
|
||||
htmlStringPlugin.enforce = 'post'
|
||||
|
||||
const entries = []
|
||||
for (const dirent of await readdir(extensionsRoot, { withFileTypes: true })) {
|
||||
if (!dirent.isDirectory() || !dirent.name.startsWith('ext-')) continue
|
||||
const entryPath = join(extensionsRoot, dirent.name, `${dirent.name}.js`)
|
||||
try {
|
||||
const st = await stat(entryPath)
|
||||
if (st.isFile()) entries.push(entryPath)
|
||||
} catch (_err) {
|
||||
// no entry file, skip
|
||||
}
|
||||
}
|
||||
|
||||
if (!entries.length) {
|
||||
console.info('No extension entries found')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
await build({
|
||||
// Use isolated config to avoid inheriting the main lib/iife build.
|
||||
configFile: false,
|
||||
root,
|
||||
base: './',
|
||||
logLevel: 'info',
|
||||
plugins: [
|
||||
{
|
||||
name: 'svgedit-skip-vite-build-html',
|
||||
apply: 'build',
|
||||
enforce: 'pre',
|
||||
configResolved (config) {
|
||||
config.plugins = config.plugins.filter(plugin => plugin.name !== 'vite:build-html')
|
||||
}
|
||||
},
|
||||
htmlStringPlugin,
|
||||
{
|
||||
...dynamicImportVars({
|
||||
include: ['src/editor/extensions/*/*.js']
|
||||
}),
|
||||
apply: 'build'
|
||||
}
|
||||
],
|
||||
build: {
|
||||
outDir,
|
||||
emptyOutDir: false, // main build already wrote Editor.js, keep it
|
||||
sourcemap: true,
|
||||
minify: false, // keep exports intact
|
||||
rollupOptions: {
|
||||
treeshake: false,
|
||||
preserveEntrySignatures: 'strict',
|
||||
input: entries,
|
||||
output: {
|
||||
format: 'es',
|
||||
inlineDynamicImports: false,
|
||||
preserveModules: true,
|
||||
preserveModulesRoot: extensionsRoot,
|
||||
entryFileNames: '[name].js',
|
||||
chunkFileNames: 'extensions/_chunks/[name]-[hash].js',
|
||||
assetFileNames: 'extensions/_assets/[name]-[hash][extname]'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
console.info(`Bundled ${entries.length} extensions`)
|
||||
25
scripts/copy-static.mjs
Normal file
25
scripts/copy-static.mjs
Normal file
@@ -0,0 +1,25 @@
|
||||
import { cp, mkdir } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
const root = process.cwd()
|
||||
const outDir = resolve(root, 'dist/editor')
|
||||
|
||||
await mkdir(outDir, { recursive: true })
|
||||
|
||||
const targets = [
|
||||
['src/editor/index.html', 'index.html'],
|
||||
['src/editor/xdomain-index.html', 'xdomain-index.html'],
|
||||
['src/editor/iife-index.html', 'iife-index.html'],
|
||||
['src/editor/browser-not-supported.html', 'browser-not-supported.html'],
|
||||
['src/editor/browser-not-supported.js', 'browser-not-supported.js'],
|
||||
['src/editor/svgedit.css', 'svgedit.css'],
|
||||
['src/editor/images', 'images'],
|
||||
['src/editor/components/jgraduate/images', 'components/jgraduate/images'],
|
||||
['src/editor/extensions', 'extensions']
|
||||
]
|
||||
|
||||
for (const [src, dest] of targets) {
|
||||
await cp(resolve(root, src), resolve(outDir, dest), { recursive: true })
|
||||
}
|
||||
|
||||
console.info('Copied static editor assets to dist/editor')
|
||||
44
scripts/run-e2e.mjs
Normal file
44
scripts/run-e2e.mjs
Normal file
@@ -0,0 +1,44 @@
|
||||
import { spawn } from 'node:child_process'
|
||||
import { join } from 'node:path'
|
||||
import { existsSync } from 'node:fs'
|
||||
|
||||
// Always instrument the build for coverage and ensure Playwright can launch.
|
||||
process.env.COVERAGE = 'true'
|
||||
// Put Playwright browsers inside the project so CI without sudo/system cache still works.
|
||||
const playwrightCache = process.env.PLAYWRIGHT_BROWSERS_PATH ||
|
||||
join(process.cwd(), 'node_modules', '.cache', 'ms-playwright')
|
||||
process.env.PLAYWRIGHT_BROWSERS_PATH = playwrightCache
|
||||
const sanitizedEnv = { ...process.env, PLAYWRIGHT_BROWSERS_PATH: playwrightCache }
|
||||
delete sanitizedEnv.ELECTRON_RUN_AS_NODE
|
||||
delete process.env.ELECTRON_RUN_AS_NODE
|
||||
|
||||
const run = (cmd, args, opts = {}) => new Promise((resolve, reject) => {
|
||||
const child = spawn(cmd, args, { stdio: 'inherit', shell: false, env: sanitizedEnv, ...opts })
|
||||
child.on('exit', code => (code === 0 ? resolve() : reject(new Error(`${cmd} exited with code ${code}`))))
|
||||
child.on('error', reject)
|
||||
})
|
||||
|
||||
const hasPlaywright = async () => {
|
||||
try {
|
||||
await run('npx', ['playwright', '--version'], { timeout: 30000 })
|
||||
return true
|
||||
} catch (error) {
|
||||
console.warn('Skipping e2e tests because Playwright is unavailable or failed to verify.')
|
||||
console.warn(error.message || error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const ensureBrowser = async () => {
|
||||
// Download Chromium to the project cache if it's missing.
|
||||
if (!existsSync(playwrightCache)) {
|
||||
await run('npx', ['playwright', 'install', 'chromium'])
|
||||
}
|
||||
}
|
||||
|
||||
if (await hasPlaywright()) {
|
||||
await ensureBrowser()
|
||||
await run('rimraf', ['.nyc_output/*'], { shell: true })
|
||||
await run('npx', ['playwright', 'test'])
|
||||
await run('npx', ['nyc', 'report', '--reporter', 'text-summary', '--reporter', 'json-summary'])
|
||||
}
|
||||
Reference in New Issue
Block a user