Feat: Vite + React (#991)

* remove test_web

* integrate reactjs-vite

* update lint-staged

* simple cors proxy server which handles cookies and redirections

* overrider global fetch with proxy call

* simple examples for set cookies and fetch request

* app bar

* add redux

* loadPlugins.
error: cant load urlencode module

* add ts-node for transforming

* plugin search & select

* plugin slice

* accordion components

* path item & popular novel

* optional for filters

* search novel

* parse novel

* parseChapter

* update cookies

* default filters

* fix: import path

* bundler module resolution

* new json_plugins

* fix: generate multisrc (missing filters)

* fix: host scripts

* fix: plugin code path

* remove typechecking action
This commit is contained in:
nyagami
2024-06-16 18:13:41 +07:00
committed by GitHub
parent a4cb781f32
commit d36e8ec85f
333 changed files with 5857 additions and 28579 deletions
@@ -1,14 +1,10 @@
require('module-alias/register');
import path from 'path';
import fs from 'fs';
import { exit } from 'process';
import languages from './languages.js';
import { execSync } from 'child_process';
import { minify } from './terser.js';
import * as fs from 'fs';
import { languages } from '@libs/languages';
import * as path from 'path';
import { Plugin } from '@typings/plugin';
import { minify } from './terser';
const root = path.dirname(__dirname);
const outRoot = path.join(root, '..');
const { execSync } = require('child_process');
const REMOTE = execSync('git remote get-url origin')
.toString()
.replace(/[\s\n]/g, '');
@@ -21,48 +17,69 @@ const USERNAME = matched[1];
const REPO = matched[2];
const USER_CONTENT_LINK = `https://raw.githubusercontent.com/${USERNAME}/${REPO}/${CURRENT_BRANCH}`;
const ICON_LINK = `${USER_CONTENT_LINK}/icons`;
const PLUGIN_LINK = `${USER_CONTENT_LINK}/.js/plugins`;
const ICON_LINK = `${USER_CONTENT_LINK}/public/icons`;
const PLUGIN_LINK = `${USER_CONTENT_LINK}/.js/src/plugins`;
const json: HostedPluginItem[] = [];
if (!fs.existsSync(path.join(outRoot, '.dist'))) {
fs.mkdirSync(path.join(outRoot, '.dist'));
const DIST_DIR = '.dist';
const json = [];
if (!fs.existsSync(DIST_DIR)) {
fs.mkdirSync(DIST_DIR);
}
const jsonPath = path.join(outRoot, '.dist', 'plugins.json');
const jsonMinPath = path.join(outRoot, '.dist', 'plugins.min.json');
const jsonPath = path.join(DIST_DIR, 'plugins.json');
const jsonMinPath = path.join(DIST_DIR, 'plugins.min.json');
const pluginSet = new Set();
let totalPlugins = 0;
interface HostedPluginItem {
id: string;
name: string;
site: string;
lang: string;
version: string;
url: string;
iconUrl: string;
}
const createRecursiveProxy = () => {
const target = {};
const handler = {
get(target, prop) {
if (prop === 'get') {
return a => a;
}
if (!target[prop]) {
target[prop] = createRecursiveProxy();
}
return target[prop];
},
};
return new Proxy(target, handler);
};
const proxy = createRecursiveProxy();
const _require = () => proxy;
const COMPILED_PLUGIN_DIR = './.js/src/plugins';
for (let language in languages) {
// language with English name
const langPath = path.join(root, 'plugins', language.toLowerCase());
const langPath = path.join(COMPILED_PLUGIN_DIR, language.toLowerCase());
if (!fs.existsSync(langPath)) continue;
const plugins = fs.readdirSync(langPath);
plugins.forEach(plugin => {
if (plugin.startsWith('.')) return;
minify(path.join(langPath, plugin));
const instance: Plugin.PluginBase = require(
`../plugins/${language.toLowerCase()}/${plugin.split('.')[0]}`,
).default;
const rawCode = fs.readFileSync(
`${COMPILED_PLUGIN_DIR}/${language.toLowerCase()}/${plugin}`,
'utf-8',
);
const instance = Function(
'require',
'module',
`const exports = module.exports = {};
${rawCode};
return exports.default`,
)(_require, {});
const { id, name, site, version, icon } = instance;
const normalisedName = name.replace(/\[.*\]/, '');
const info: HostedPluginItem = {
const info = {
id,
name: normalisedName,
site,
lang: languages[language as keyof typeof languages],
lang: languages[language],
version,
url: `${PLUGIN_LINK}/${language.toLowerCase()}/${plugin}`,
iconUrl: `${ICON_LINK}/${icon || 'siteNotAvailable.png'}`,
@@ -97,7 +114,7 @@ fs.writeFileSync(
// check for broken plugins
for (let language in languages) {
const tsFiles = fs.readdirSync(
path.join(root, '..', 'plugins', language.toLocaleLowerCase()),
path.join('./src/plugins', language.toLocaleLowerCase()),
);
tsFiles
.filter(f => f.endsWith('.broken.ts'))
+17
View File
@@ -0,0 +1,17 @@
export default {
Arabic: '‎العربية',
Chinese: '中文, 汉语, 漢語',
English: 'English',
French: 'Français',
Indonesian: 'Bahasa Indonesia',
Japanese: '日本語',
Korean: '조선말, 한국어',
Polish: 'Polski',
Portuguese: 'Português',
Russian: 'Русский',
Spanish: 'Español',
Thai: 'ไทย',
Turkish: 'Türkçe',
Ukrainian: 'Українська',
Vietnamese: 'Tiếng Việt',
};
@@ -1,35 +1,30 @@
import path from 'path';
import fs from 'fs';
type GeneratedScript = {
lang: string;
filename: string;
pluginScript: string;
};
// type GeneratedScript = {
// lang: string;
// filename: string;
// pluginScript: string;
// };
export type ScrpitGeneratorFunction = (name: string) => GeneratedScript[];
// export type ScrpitGeneratorFunction = () => GeneratedScript[];
const isScriptGenerator = (s: unknown): s is ScrpitGeneratorFunction => {
const isScriptGenerator = s => {
return !!s && typeof s === 'function';
};
const generate = async (name: string): Promise<boolean> => {
const generate = async name => {
try {
const generateAll: ScrpitGeneratorFunction | unknown = require(
`./${name}/generator`,
).generateAll;
const generateAll = (await import(`./${name}/generator.js`)).generateAll;
if (!isScriptGenerator(generateAll)) return false;
const sources = generateAll(name);
const sources = generateAll();
for (let source of sources) {
const { lang, filename, pluginScript } = source;
if (!lang || !filename || !pluginScript) {
console.warn(name, ': lang, filename, pluginScript are required!');
continue;
}
const pluginsDir = path.join(
path.dirname(path.dirname(__dirname)),
'plugins',
);
const pluginsDir = './src/plugins';
const filePath = path.join(
pluginsDir,
lang.toLowerCase(),
@@ -39,17 +34,19 @@ const generate = async (name: string): Promise<boolean> => {
}
return true;
} catch (e) {
console.log(`${name} is broken! ${e}`);
console.log(`${name} is broken! ${e}\n`);
return false;
}
};
const MULTISRC_DIR = './scripts/multisrc';
const run = async () => {
const sources = fs
.readdirSync(__dirname)
.readdirSync(MULTISRC_DIR)
.filter(
name =>
fs.lstatSync(path.join(__dirname, name)).isDirectory() &&
fs.lstatSync(path.join(MULTISRC_DIR, name)).isDirectory() &&
!name.endsWith('.broken'),
);
@@ -1,7 +1,7 @@
import * as fs from 'fs';
import * as cheerio from 'cheerio';
import * as path from 'path';
import list from './sources.json';
import list from './sources.json' with { type: 'json' };
import { HotNovelPubMetadata } from './template';
async function getFilters(sources: HotNovelPubMetadata) {
@@ -1,11 +1,16 @@
import { FilterTypes, Filters } from '../../../libs/filterInputs';
import { ScrpitGeneratorFunction } from '../generate';
import list from './sources.json';
import { HotNovelPubMetadata } from './template';
import list from './sources.json' with { type: 'json' };
import { readFileSync } from 'fs';
import path from 'path';
const lang: { [key: string]: string } = {
const FilterTypes = {
TextInput: 'Text',
Picker: 'Picker',
CheckboxGroup: 'Checkbox',
Switch: 'Switch',
ExcludableCheckboxGroup: 'XCheckbox',
};
const lang = {
en: 'english',
ru: 'russian',
es: 'spanish',
@@ -13,12 +18,12 @@ const lang: { [key: string]: string } = {
th: 'turkish',
};
export const generateAll: ScrpitGeneratorFunction = function (name) {
export const generateAll = function () {
return list
.map<HotNovelPubMetadata>(p => {
const filters: Filters = {};
.map(p => {
const filters = {};
for (const k in p.filters) {
const f = p.filters[k as keyof typeof p.filters];
const f = p.filters[k];
if (f) {
filters[k] = {
...f,
@@ -28,15 +33,15 @@ export const generateAll: ScrpitGeneratorFunction = function (name) {
}
return { ...p, filters };
})
.map((metadata: HotNovelPubMetadata) => {
console.log(`[${name}]: Generating`, metadata.id);
.map(metadata => {
console.log(`[hotnovelpub]: Generating`, metadata.id);
return generator(metadata);
});
};
const generator = function generator(metadata: HotNovelPubMetadata) {
const generator = function generator(metadata) {
const HotNovelPubTemplate = readFileSync(
path.join(__dirname, 'template.ts'),
'./scripts/multisrc/hotnovelpub/template.ts',
{
encoding: 'utf-8',
},
+36
View File
@@ -0,0 +1,36 @@
import list from './sources.json' with { type: 'json' };
import defaultSettings from './settings.json' with { type: 'json' };
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll = function () {
return list.map(metadata => {
metadata.filters = Object.assign(defaultSettings.filters, metadata.filters);
console.log(`[ifreedom]: Generating`, metadata.id);
return generator(metadata);
});
};
const generator = function generator(metadata) {
const IfreedomTemplate = readFileSync(
'./scripts/multisrc/ifreedom/template.ts',
{
encoding: 'utf-8',
},
);
const pluginScript = `
${IfreedomTemplate}
const plugin = new IfreedomPlugin(${JSON.stringify(metadata).replace(
/"type":"([^"]+)"/g,
'"type":FilterTypes.$1',
)});
export default plugin;
`.trim();
return {
lang: 'russian',
filename: metadata.sourceName,
pluginScript,
};
};
-36
View File
@@ -1,36 +0,0 @@
import { Filters } from '../../../libs/filterInputs';
import { ScrpitGeneratorFunction } from '../generate';
import list from './sources.json';
import defaultSettings from './settings.json';
import { IfreedomMetadata } from './template';
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll: ScrpitGeneratorFunction = function (name) {
return list.map((metadata: IfreedomMetadata) => {
metadata.filters = Object.assign(defaultSettings.filters, metadata.filters);
console.log(`[${name}]: Generating`, metadata.id);
return generator(metadata);
});
};
const generator = function generator(metadata: IfreedomMetadata) {
const IfreedomTemplate = readFileSync(path.join(__dirname, 'template.ts'), {
encoding: 'utf-8',
});
const pluginScript = `
${IfreedomTemplate}
const plugin = new IfreedomPlugin(${JSON.stringify(metadata).replace(
/"type":"([^"]+)"/g,
'"type":FilterTypes.$1',
)});
export default plugin;
`.trim();
return {
lang: 'russian',
filename: metadata.sourceName,
pluginScript,
};
};
@@ -0,0 +1,37 @@
import list from './sources.json' with { type: 'json' };
import { readFileSync } from 'fs';
export const generateAll = function () {
return list.map(source => {
let filters = {};
try {
filters = require(`./filters/${source.id}`);
source.filters = filters.filters;
} catch (e) {}
console.log(
`[lightnovelworld] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
);
return generator(source);
});
};
const generator = function generator(source) {
const LightNovelWPTemplate = readFileSync(
'./scripts/multisrc/lightnovelworld/template.ts',
{
encoding: 'utf-8',
},
);
const pluginScript = `
${LightNovelWPTemplate}
const plugin = new LightNovelWorld(${JSON.stringify(source)});
export default plugin;
`.trim();
return {
lang: source.options?.lang || 'English',
filename: source.sourceName,
pluginScript,
};
};
@@ -1,40 +0,0 @@
import { ScrpitGeneratorFunction } from '../generate';
import { LightNovelWorldMetadata } from './template';
import list from './sources.json';
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll: ScrpitGeneratorFunction = function (name) {
return list.map((source: LightNovelWorldMetadata) => {
let filters: any = {};
try {
filters = require(`./filters/${source.id}`);
source.filters = filters.filters;
} catch (e) {}
console.log(
`[${name}] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
);
return generator(source);
});
};
const generator = function generator(source: LightNovelWorldMetadata) {
const LightNovelWPTemplate = readFileSync(
path.join(__dirname, 'template.ts'),
{
encoding: 'utf-8',
},
);
const pluginScript = `
${LightNovelWPTemplate}
const plugin = new LightNovelWorld(${JSON.stringify(source)});
export default plugin;
`.trim();
return {
lang: source.options?.lang || 'English',
filename: source.sourceName,
pluginScript,
};
};
@@ -1,26 +1,23 @@
import { ScrpitGeneratorFunction } from '../generate';
import { LightNovelWPMetadata } from './template';
import list from './sources.json';
import list from './sources.json' with { type: 'json' };
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll: ScrpitGeneratorFunction = function (name) {
return list.map((source: LightNovelWPMetadata) => {
let filters: any = {};
export const generateAll = function () {
return list.map(source => {
let filters = {};
try {
filters = require(`./filters/${source.id}`);
source.filters = filters.filters;
} catch (e) {}
console.log(
`[${name}] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
`[lightnovelwp] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
);
return generator(source);
});
};
const generator = function generator(source: LightNovelWPMetadata) {
const generator = function generator(source) {
const LightNovelWPTemplate = readFileSync(
path.join(__dirname, 'template.ts'),
'./scripts/multisrc/lightnovelwp/template.ts',
{
encoding: 'utf-8',
},
+33
View File
@@ -0,0 +1,33 @@
import list from './sources.json' with { type: 'json' };
import { readFileSync } from 'fs';
export const generateAll = function () {
return list.map(metadata => {
let filters = {};
try {
filters = require(`./filters/${metadata.id}`);
metadata.filters = filters.filters;
} catch (e) {}
console.log(
`[madara] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)} ${metadata.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
);
return generator(metadata);
});
};
const generator = function generator(metadata) {
const madaraTemplate = readFileSync('./scripts/multisrc/madara/template.ts', {
encoding: 'utf-8',
});
const pluginScript = `
${madaraTemplate}
const plugin = new MadaraPlugin(${JSON.stringify(metadata)});
export default plugin;
`.trim();
return {
lang: metadata.options?.lang || 'English',
filename: metadata.sourceName,
pluginScript,
};
};
-37
View File
@@ -1,37 +0,0 @@
import { FilterTypes, Filters } from '../../../libs/filterInputs';
import { ScrpitGeneratorFunction } from '../generate';
import list from './sources.json';
import { MadaraMetadata } from './template';
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll: ScrpitGeneratorFunction = function (name) {
return list.map((metadata: MadaraMetadata) => {
let filters: any = {};
try {
filters = require(`./filters/${metadata.id}`);
metadata.filters = filters.filters;
} catch (e) {}
console.log(
`[${name}] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)} ${metadata.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
);
return generator(metadata);
});
};
const generator = function generator(metadata: MadaraMetadata) {
const madaraTemplate = readFileSync(path.join(__dirname, 'template.ts'), {
encoding: 'utf-8',
});
const pluginScript = `
${madaraTemplate}
const plugin = new MadaraPlugin(${JSON.stringify(metadata)});
export default plugin;
`.trim();
return {
lang: metadata.options?.lang || 'English',
filename: metadata.sourceName,
pluginScript,
};
};
@@ -1,22 +1,20 @@
import { readFileSync } from 'fs';
import path from 'path';
import list from './sources.json';
import { ReadNovelFullMetadata } from './template';
import { ScrpitGeneratorFunction } from '../generate';
import list from './sources.json' with { type: 'json' };
export const generateAll: ScrpitGeneratorFunction = function (name) {
return list.map((metadata: ReadNovelFullMetadata) => {
export const generateAll = function () {
return list.map(metadata => {
console.log(
`[${name}] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)}`,
`[readnovelfull] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)}`,
);
return generator(metadata);
});
};
const generator = function generator(metadata: ReadNovelFullMetadata) {
const generator = function generator(metadata) {
const readNovelFullTemplate = readFileSync(
path.join(__dirname, 'template.ts'),
'./scripts/multisrc/readnovelfull/template.ts',
{ encoding: 'utf-8' },
);
+34
View File
@@ -0,0 +1,34 @@
import list from './sources.json' with { type: 'json' };
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll = function () {
return list.map(metadata => {
try {
const { filters } = require(`./filters/${metadata.id}`);
metadata.filters = filters;
} catch (e) {}
console.log(
`[readwn] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)} ${metadata.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
);
return generator(metadata);
});
};
const generator = function generator(metadata) {
const readwnTemplate = readFileSync('./scripts/multisrc/readwn/template.ts', {
encoding: 'utf-8',
});
const pluginScript = `
${readwnTemplate}
const plugin = new ReadwnPlugin(${JSON.stringify(metadata)});
export default plugin;
`.trim();
return {
lang: 'english',
filename: metadata.sourceName,
pluginScript,
};
};
-36
View File
@@ -1,36 +0,0 @@
import { ScrpitGeneratorFunction } from '../generate';
import { ReadwnMetadata } from './template';
import list from './sources.json';
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll: ScrpitGeneratorFunction = function (name) {
return list.map((metadata: ReadwnMetadata) => {
try {
const { filters }: any = require(`./filters/${metadata.id}`);
metadata.filters = filters;
} catch (e) {}
console.log(
`[${name}] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)} ${metadata.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
);
return generator(metadata);
});
};
const generator = function generator(metadata: ReadwnMetadata) {
const readwnTemplate = readFileSync(path.join(__dirname, 'template.ts'), {
encoding: 'utf-8',
});
const pluginScript = `
${readwnTemplate}
const plugin = new ReadwnPlugin(${JSON.stringify(metadata)});
export default plugin;
`.trim();
return {
lang: 'english',
filename: metadata.sourceName,
pluginScript,
};
};
@@ -1,40 +1,44 @@
import { Filters, FilterTypes } from '../../../libs/filterInputs';
import { ScrpitGeneratorFunction } from '../generate';
import list from './sources.json';
import defaultSettings from './settings.json';
import { RulateMetadata } from './template';
import list from './sources.json' with { type: 'json' };
import defaultSettings from './settings.json' with { type: 'json' };
import { readFileSync } from 'fs';
import path from 'path';
export const generateAll: ScrpitGeneratorFunction = function (name) {
const FilterTypes = {
TextInput: 'Text',
Picker: 'Picker',
CheckboxGroup: 'Checkbox',
Switch: 'Switch',
ExcludableCheckboxGroup: 'XCheckbox',
};
export const generateAll = function () {
return list
.map<RulateMetadata>(p => {
.map(p => {
if (p.filters) {
p.filters.cat.options.unshift(...defaultSettings.filters.cat.options);
}
p.filters = Object.assign(defaultSettings.filters, p.filters);
let d = false;
const filters: any = {};
const filters = {};
for (const k in p.filters) {
const f = p.filters[k as keyof typeof p.filters];
const f = p.filters[k];
if (f) {
filters[k] = {
...f,
type: FilterTypes[f.type as keyof typeof FilterTypes],
type: FilterTypes[f.type],
};
}
}
return { ...p, filters: d ? undefined : filters };
})
.map((metadata: RulateMetadata) => {
console.log(`[${name}]: Generating`, metadata.id);
.map(metadata => {
console.log(`[rulate]: Generating`, metadata.id);
return generator(metadata);
});
};
const generator = function generator(metadata: RulateMetadata) {
const rulateTemplate = readFileSync(path.join(__dirname, 'template.ts'), {
const generator = function generator(metadata) {
const rulateTemplate = readFileSync('./scripts/multisrc/rulate/template.ts', {
encoding: 'utf-8',
});
+5 -3
View File
@@ -1,5 +1,5 @@
const { minify_sync } = require('terser');
import * as fs from 'fs';
import { minify_sync } from 'terser';
import fs from 'fs';
const config = {
compress: {
@@ -12,8 +12,10 @@ const config = {
toplevel: true,
};
export const minify = function (path: string) {
const minify = function (path) {
const code = fs.readFileSync(path).toString();
const result = minify_sync(code, config);
fs.writeFileSync(path, result.code);
};
export { minify };