diff --git a/electron.vite.config.mts b/electron.vite.config.mts index cbe9b479..73caa775 100644 --- a/electron.vite.config.mts +++ b/electron.vite.config.mts @@ -1,16 +1,17 @@ import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { UserConfig } from 'vite'; import { defineConfig, defineViteConfig } from 'electron-vite'; import builtinModules from 'builtin-modules'; -import viteResolve from 'vite-plugin-resolve'; + import Inspect from 'vite-plugin-inspect'; import solidPlugin from 'vite-plugin-solid'; +import viteResolve from 'vite-plugin-resolve'; + +import { withFilter, type UserConfig } from 'vite'; import { pluginVirtualModuleGenerator } from './vite-plugins/plugin-importer.mjs'; import pluginLoader from './vite-plugins/plugin-loader.mjs'; - import { i18nImporter } from './vite-plugins/i18n-importer.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -23,6 +24,9 @@ const resolveAlias = { export default defineConfig({ main: defineViteConfig(({ mode }) => { const commonConfig: UserConfig = { + experimental: { + enableNativePlugin: true, + }, plugins: [ pluginLoader('backend'), viteResolve({ @@ -72,6 +76,9 @@ export default defineConfig({ }), preload: defineViteConfig(({ mode }) => { const commonConfig: UserConfig = { + experimental: { + enableNativePlugin: true, + }, plugins: [ pluginLoader('preload'), viteResolve({ @@ -120,13 +127,18 @@ export default defineConfig({ }), renderer: defineViteConfig(({ mode }) => { const commonConfig: UserConfig = { + experimental: { + enableNativePlugin: mode !== 'development', // Disable native plugin in development mode to avoid issues with HMR (bug in rolldown-vite) + }, plugins: [ pluginLoader('renderer'), viteResolve({ 'virtual:i18n': i18nImporter(), 'virtual:plugins': pluginVirtualModuleGenerator('renderer'), }), - solidPlugin(), + withFilter(solidPlugin(), { + load: { id: [/\.(tsx|jsx)$/, '/@solid-refresh'] }, + }), ], root: './src/', build: { diff --git a/vite-plugins/plugin-loader.mts b/vite-plugins/plugin-loader.mts index 1bbc4a61..98e71906 100644 --- a/vite-plugins/plugin-loader.mts +++ b/vite-plugins/plugin-loader.mts @@ -2,7 +2,6 @@ import { readFileSync } from 'node:fs'; import { resolve, basename, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { createFilter } from 'vite'; import { Project, ts, @@ -39,108 +38,135 @@ const getPropertyName = (prop: Node): string | null => { export default function ( mode: 'backend' | 'preload' | 'renderer' | 'none', ): PluginOption { - const pluginFilter = createFilter([ - 'src/plugins/*/index.{js,ts}', - 'src/plugins/*', - ]); - return { name: 'ytm-plugin-loader', - load(id) { - if (!pluginFilter(id)) return null; + load: { + filter: { + id: /(?:\/plugins\/[^/]+\/index\.(?:js|ts|jsx|tsx)|\/plugins\/[^/]+\.(?:js|ts|jsx|tsx))$/, + }, + handler(id) { + const fileContent = readFileSync(id, 'utf8'); + // Create or update source file in the global project instance + const src = globalProject.createSourceFile( + '_pf' + basename(id), + fileContent, + { overwrite: true }, + ); - // Read file asynchronously - const fileContent = readFileSync(id, 'utf8'); - // Create or update source file in the global project instance - const src = globalProject.createSourceFile( - '_pf' + basename(id), - fileContent, - { overwrite: true }, - ); + let objExpr: ObjectLiteralExpression | undefined; - const exports = src.getExportedDeclarations(); - let objExpr: ObjectLiteralExpression | undefined; + // Check for `export default ...` + const defaultExportAssignment = src.getExportAssignment( + (ea) => !ea.isExportEquals(), // Filter out `export = ` + ); - // Identify the default export as an object literal, or via a 'createPlugin' call - for (const [exportName, declarations] of exports) { - if (exportName !== 'default') continue; - const expr = declarations[0]; - - const exprKind = expr.getKind(); - if (exprKind === ts.SyntaxKind.ObjectLiteralExpression) { - objExpr = expr.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression); - break; - } else if (exprKind === ts.SyntaxKind.CallExpression) { - const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression); - if ( - callExpr.getArguments().length === 1 && - callExpr.getExpression().getText() === 'createPlugin' - ) { - const arg = callExpr.getArguments()[0]; - if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) { - objExpr = arg.asKindOrThrow( - ts.SyntaxKind.ObjectLiteralExpression, - ); - break; + if (defaultExportAssignment) { + const expression = defaultExportAssignment.getExpression(); + if (expression.getKind() === ts.SyntaxKind.ObjectLiteralExpression) { + objExpr = expression.asKindOrThrow( + ts.SyntaxKind.ObjectLiteralExpression, + ); + } else if (expression.getKind() === ts.SyntaxKind.CallExpression) { + const callExpr = expression.asKindOrThrow( + ts.SyntaxKind.CallExpression, + ); + if ( + callExpr.getArguments().length === 1 && + callExpr.getExpression().getText() === 'createPlugin' + ) { + const arg = callExpr.getArguments()[0]; + if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) { + objExpr = arg.asKindOrThrow( + ts.SyntaxKind.ObjectLiteralExpression, + ); + } } } } - } - if (!objExpr) return null; - - // Build a map of property names to their AST nodes for fast lookup - const propMap = new Map(); - for (const prop of objExpr.getProperties()) { - const name = getPropertyName(prop); - if (name) propMap.set(name, prop); - } - - const contexts = ['backend', 'preload', 'renderer', 'menu']; - for (const ctx of contexts) { - if (mode === 'none' && propMap.has(ctx)) { - propMap.get(ctx)?.remove(); - continue; + // If not found via `export default`, check for a named export aliased as 'default' + if (!objExpr) { + const defaultExportDeclaration = src + .getExportedDeclarations() + .get('default'); + if (defaultExportDeclaration && defaultExportDeclaration.length > 0) { + const expr = defaultExportDeclaration[0]; + if (expr.getKind() === ts.SyntaxKind.ObjectLiteralExpression) { + objExpr = expr.asKindOrThrow( + ts.SyntaxKind.ObjectLiteralExpression, + ); + } else if (expr.getKind() === ts.SyntaxKind.CallExpression) { + const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression); + if ( + callExpr.getArguments().length === 1 && + callExpr.getExpression().getText() === 'createPlugin' + ) { + const arg = callExpr.getArguments()[0]; + if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) { + objExpr = arg.asKindOrThrow( + ts.SyntaxKind.ObjectLiteralExpression, + ); + } + } + } + } } - if (ctx === mode || (ctx === 'menu' && mode === 'backend')) continue; - if (propMap.has(ctx)) propMap.get(ctx)?.remove(); - } - // Add an exported variable 'pluginStub' with the modified object literal's text - const varStmt = src.addVariableStatement({ - isExported: true, - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: 'pluginStub', - initializer: (writer) => writer.write(objExpr.getText()), - }, - ], - }); - const stubObjExpr = varStmt - .getDeclarations()[0] - .getInitializerIfKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression); + if (!objExpr) return null; - // Similarly build a map for the stub properties - const stubMap = new Map(); - for (const prop of stubObjExpr.getProperties()) { - const name = getPropertyName(prop); - if (name) stubMap.set(name, prop); - } - - const stubContexts = - mode === 'backend' - ? contexts.filter((ctx) => ctx !== 'menu') - : contexts; - for (const ctx of stubContexts) { - if (stubMap.has(ctx)) { - stubMap.get(ctx)?.remove(); + // Build a map of property names to their AST nodes for fast lookup + const propMap = new Map(); + for (const prop of objExpr.getProperties()) { + const name = getPropertyName(prop); + if (name) propMap.set(name, prop); } - } - return { - code: src.getText(), - }; + const contexts = ['backend', 'preload', 'renderer', 'menu']; + for (const ctx of contexts) { + if (mode === 'none' && propMap.has(ctx)) { + propMap.get(ctx)?.remove(); + continue; + } + if (ctx === mode || (ctx === 'menu' && mode === 'backend')) continue; + if (propMap.has(ctx)) propMap.get(ctx)?.remove(); + } + + // Add an exported variable 'pluginStub' with the modified object literal's text + const varStmt = src.addVariableStatement({ + isExported: true, + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: 'pluginStub', + initializer: (writer) => writer.write(objExpr.getText()), + }, + ], + }); + const stubObjExpr = varStmt + .getDeclarations()[0] + .getInitializerIfKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression); + + // Similarly build a map for the stub properties + const stubMap = new Map(); + for (const prop of stubObjExpr.getProperties()) { + const name = getPropertyName(prop); + if (name) stubMap.set(name, prop); + } + + const stubContexts = + mode === 'backend' + ? contexts.filter((ctx) => ctx !== 'menu') + : contexts; + for (const ctx of stubContexts) { + if (stubMap.has(ctx)) { + stubMap.get(ctx)?.remove(); + } + } + + return { + code: src.getText(), + }; + }, }, }; }