mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
feat: enable rolldown native plugin (#3502)
This commit is contained in:
@ -1,16 +1,17 @@
|
|||||||
import { dirname, join, resolve } from 'node:path';
|
import { dirname, join, resolve } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
import { UserConfig } from 'vite';
|
|
||||||
import { defineConfig, defineViteConfig } from 'electron-vite';
|
import { defineConfig, defineViteConfig } from 'electron-vite';
|
||||||
import builtinModules from 'builtin-modules';
|
import builtinModules from 'builtin-modules';
|
||||||
import viteResolve from 'vite-plugin-resolve';
|
|
||||||
import Inspect from 'vite-plugin-inspect';
|
import Inspect from 'vite-plugin-inspect';
|
||||||
import solidPlugin from 'vite-plugin-solid';
|
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 { pluginVirtualModuleGenerator } from './vite-plugins/plugin-importer.mjs';
|
||||||
import pluginLoader from './vite-plugins/plugin-loader.mjs';
|
import pluginLoader from './vite-plugins/plugin-loader.mjs';
|
||||||
|
|
||||||
import { i18nImporter } from './vite-plugins/i18n-importer.mjs';
|
import { i18nImporter } from './vite-plugins/i18n-importer.mjs';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
@ -23,6 +24,9 @@ const resolveAlias = {
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
main: defineViteConfig(({ mode }) => {
|
main: defineViteConfig(({ mode }) => {
|
||||||
const commonConfig: UserConfig = {
|
const commonConfig: UserConfig = {
|
||||||
|
experimental: {
|
||||||
|
enableNativePlugin: true,
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
pluginLoader('backend'),
|
pluginLoader('backend'),
|
||||||
viteResolve({
|
viteResolve({
|
||||||
@ -72,6 +76,9 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
preload: defineViteConfig(({ mode }) => {
|
preload: defineViteConfig(({ mode }) => {
|
||||||
const commonConfig: UserConfig = {
|
const commonConfig: UserConfig = {
|
||||||
|
experimental: {
|
||||||
|
enableNativePlugin: true,
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
pluginLoader('preload'),
|
pluginLoader('preload'),
|
||||||
viteResolve({
|
viteResolve({
|
||||||
@ -120,13 +127,18 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
renderer: defineViteConfig(({ mode }) => {
|
renderer: defineViteConfig(({ mode }) => {
|
||||||
const commonConfig: UserConfig = {
|
const commonConfig: UserConfig = {
|
||||||
|
experimental: {
|
||||||
|
enableNativePlugin: mode !== 'development', // Disable native plugin in development mode to avoid issues with HMR (bug in rolldown-vite)
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
pluginLoader('renderer'),
|
pluginLoader('renderer'),
|
||||||
viteResolve({
|
viteResolve({
|
||||||
'virtual:i18n': i18nImporter(),
|
'virtual:i18n': i18nImporter(),
|
||||||
'virtual:plugins': pluginVirtualModuleGenerator('renderer'),
|
'virtual:plugins': pluginVirtualModuleGenerator('renderer'),
|
||||||
}),
|
}),
|
||||||
solidPlugin(),
|
withFilter(solidPlugin(), {
|
||||||
|
load: { id: [/\.(tsx|jsx)$/, '/@solid-refresh'] },
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
root: './src/',
|
root: './src/',
|
||||||
build: {
|
build: {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { readFileSync } from 'node:fs';
|
|||||||
import { resolve, basename, dirname } from 'node:path';
|
import { resolve, basename, dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
import { createFilter } from 'vite';
|
|
||||||
import {
|
import {
|
||||||
Project,
|
Project,
|
||||||
ts,
|
ts,
|
||||||
@ -39,108 +38,135 @@ const getPropertyName = (prop: Node): string | null => {
|
|||||||
export default function (
|
export default function (
|
||||||
mode: 'backend' | 'preload' | 'renderer' | 'none',
|
mode: 'backend' | 'preload' | 'renderer' | 'none',
|
||||||
): PluginOption {
|
): PluginOption {
|
||||||
const pluginFilter = createFilter([
|
|
||||||
'src/plugins/*/index.{js,ts}',
|
|
||||||
'src/plugins/*',
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'ytm-plugin-loader',
|
name: 'ytm-plugin-loader',
|
||||||
load(id) {
|
load: {
|
||||||
if (!pluginFilter(id)) return null;
|
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
|
let objExpr: ObjectLiteralExpression | undefined;
|
||||||
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 },
|
|
||||||
);
|
|
||||||
|
|
||||||
const exports = src.getExportedDeclarations();
|
// Check for `export default ...`
|
||||||
let objExpr: ObjectLiteralExpression | undefined;
|
const defaultExportAssignment = src.getExportAssignment(
|
||||||
|
(ea) => !ea.isExportEquals(), // Filter out `export = `
|
||||||
|
);
|
||||||
|
|
||||||
// Identify the default export as an object literal, or via a 'createPlugin' call
|
if (defaultExportAssignment) {
|
||||||
for (const [exportName, declarations] of exports) {
|
const expression = defaultExportAssignment.getExpression();
|
||||||
if (exportName !== 'default') continue;
|
if (expression.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
|
||||||
const expr = declarations[0];
|
objExpr = expression.asKindOrThrow(
|
||||||
|
ts.SyntaxKind.ObjectLiteralExpression,
|
||||||
const exprKind = expr.getKind();
|
);
|
||||||
if (exprKind === ts.SyntaxKind.ObjectLiteralExpression) {
|
} else if (expression.getKind() === ts.SyntaxKind.CallExpression) {
|
||||||
objExpr = expr.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
|
const callExpr = expression.asKindOrThrow(
|
||||||
break;
|
ts.SyntaxKind.CallExpression,
|
||||||
} else if (exprKind === ts.SyntaxKind.CallExpression) {
|
);
|
||||||
const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression);
|
if (
|
||||||
if (
|
callExpr.getArguments().length === 1 &&
|
||||||
callExpr.getArguments().length === 1 &&
|
callExpr.getExpression().getText() === 'createPlugin'
|
||||||
callExpr.getExpression().getText() === 'createPlugin'
|
) {
|
||||||
) {
|
const arg = callExpr.getArguments()[0];
|
||||||
const arg = callExpr.getArguments()[0];
|
if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
|
||||||
if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
|
objExpr = arg.asKindOrThrow(
|
||||||
objExpr = arg.asKindOrThrow(
|
ts.SyntaxKind.ObjectLiteralExpression,
|
||||||
ts.SyntaxKind.ObjectLiteralExpression,
|
);
|
||||||
);
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!objExpr) return null;
|
// If not found via `export default`, check for a named export aliased as 'default'
|
||||||
|
if (!objExpr) {
|
||||||
// Build a map of property names to their AST nodes for fast lookup
|
const defaultExportDeclaration = src
|
||||||
const propMap = new Map<string, ObjectLiteralElementLike>();
|
.getExportedDeclarations()
|
||||||
for (const prop of objExpr.getProperties()) {
|
.get('default');
|
||||||
const name = getPropertyName(prop);
|
if (defaultExportDeclaration && defaultExportDeclaration.length > 0) {
|
||||||
if (name) propMap.set(name, prop);
|
const expr = defaultExportDeclaration[0];
|
||||||
}
|
if (expr.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
|
||||||
|
objExpr = expr.asKindOrThrow(
|
||||||
const contexts = ['backend', 'preload', 'renderer', 'menu'];
|
ts.SyntaxKind.ObjectLiteralExpression,
|
||||||
for (const ctx of contexts) {
|
);
|
||||||
if (mode === 'none' && propMap.has(ctx)) {
|
} else if (expr.getKind() === ts.SyntaxKind.CallExpression) {
|
||||||
propMap.get(ctx)?.remove();
|
const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression);
|
||||||
continue;
|
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
|
if (!objExpr) return null;
|
||||||
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
|
// Build a map of property names to their AST nodes for fast lookup
|
||||||
const stubMap = new Map<string, ObjectLiteralElementLike>();
|
const propMap = new Map<string, ObjectLiteralElementLike>();
|
||||||
for (const prop of stubObjExpr.getProperties()) {
|
for (const prop of objExpr.getProperties()) {
|
||||||
const name = getPropertyName(prop);
|
const name = getPropertyName(prop);
|
||||||
if (name) stubMap.set(name, prop);
|
if (name) propMap.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 {
|
const contexts = ['backend', 'preload', 'renderer', 'menu'];
|
||||||
code: src.getText(),
|
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<string, ObjectLiteralElementLike>();
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user