meta: integrate vite, remove webpack
This commit is contained in:
parent
dc68771232
commit
9512210624
|
@ -82,7 +82,6 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"import/no-default-export": "warn",
|
|
||||||
"import/first": "warn",
|
"import/first": "warn",
|
||||||
"import/order": [
|
"import/order": [
|
||||||
"warn",
|
"warn",
|
||||||
|
@ -98,9 +97,11 @@
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["src/**/*.*"],
|
"files": ["src/**/*"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": "warn"
|
"no-console": "warn",
|
||||||
|
|
||||||
|
"import/no-default-export": "warn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -222,7 +223,7 @@
|
||||||
"processor": "svelte3/svelte3"
|
"processor": "svelte3/svelte3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["src/shared/types/**/*"],
|
"files": ["src/shared/**/*"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-magic-numbers": "off"
|
"@typescript-eslint/no-magic-numbers": "off"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ node_modules
|
||||||
/src/**/*.js
|
/src/**/*.js
|
||||||
/src/**/*.js.map
|
/src/**/*.js.map
|
||||||
/frontend
|
/frontend
|
||||||
|
/index.html
|
||||||
|
|
||||||
# created by testing
|
# created by testing
|
||||||
/test-paths
|
/test-paths
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
!*.md
|
!*.md
|
||||||
!*.ts
|
!*.ts
|
||||||
!*.js
|
!*.js
|
||||||
|
!*.mjs
|
||||||
!*.drawio
|
!*.drawio
|
||||||
|
|
||||||
# but ignore stuff from .gitignore
|
# but ignore stuff from .gitignore
|
||||||
|
|
|
@ -186,17 +186,20 @@ This also means potentially updating:
|
||||||
|
|
||||||
- the `node` version key under `engines` in [package.json](package.json)
|
- the `node` version key under `engines` in [package.json](package.json)
|
||||||
- the `@types/node` package
|
- the `@types/node` package
|
||||||
- the electron version in the `target` field of the [webpack config](scripts/webpack.config.js)
|
- the chrome version in the `build.target` field of the [vite config](scripts/vite.config.mjs)
|
||||||
- the `compilerOptions.target` fields of the [main](tsconfig.json) and [renderer](tsconfig.renderer.json) typescript configuration files
|
- the `compilerOptions.target` fields of the [main](tsconfig.json) and [renderer](tsconfig.renderer.json) typescript configuration files
|
||||||
- the `parserOptions.ecmaVersion` field in the [ESLint configuration](.eslintrc.json)
|
- the `parserOptions.ecmaVersion` field in the [ESLint configuration](.eslintrc.json)
|
||||||
|
|
||||||
|
Run `npm run electron-version` to find out which specific version is currently running.
|
||||||
|
|
||||||
Also might be worth checking out https://github.com/electron/electron/issues/21457 if the project can be converted to transpile to ES Modules.
|
Also might be worth checking out https://github.com/electron/electron/issues/21457 if the project can be converted to transpile to ES Modules.
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
|
||||||
#### Translation
|
#### Translation
|
||||||
|
|
||||||
With the help of [intl-messageformat](https://www.npmjs.com/package/intl-messageformat) frontend message can be defined in the [ICU Message Format](https://unicode-org.github.io/icu/userguide/format_parse/messages/). The corresponding code can be found [here](src/shared/services/translation/t.ts). I don't think a language other than English will be necessary, but the formatting features are nice anyway.
|
With the help of [intl-messageformat](https://www.npmjs.com/package/intl-messageformat) frontend messages can be defined in the [ICU Message Format](https://unicode-org.github.io/icu/userguide/format_parse/messages/). The corresponding code can be found [here](src/renderer/services/translation/t.ts). I don't think a language other than English will be necessary, but the formatting features are nice anyway.
|
||||||
|
This code is duplicated in [src/main/modules/translation/t.ts](src/main/modules/translation/t.ts), because one is run in the browser, the other in node and it led to complications with different tsconfig options.
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
|
@ -10,6 +10,7 @@
|
||||||
"email": "xymorot@mailbox.org"
|
"email": "xymorot@mailbox.org"
|
||||||
},
|
},
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
|
"module": "src/renderer.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14",
|
"node": "14",
|
||||||
"npm": "7"
|
"npm": "7"
|
||||||
|
@ -24,14 +25,18 @@
|
||||||
"typeorm:migrate": "npm run typeorm:migrate:library && npm run typeorm:migrate:store",
|
"typeorm:migrate": "npm run typeorm:migrate:library && npm run typeorm:migrate:store",
|
||||||
"typeorm:migrate:library": "typeorm migration:run -c library",
|
"typeorm:migrate:library": "typeorm migration:run -c library",
|
||||||
"typeorm:migrate:store": "typeorm migration:run -c store",
|
"typeorm:migrate:store": "typeorm migration:run -c store",
|
||||||
"build:webpack": "webpack --config scripts/webpack.config.js",
|
"build:frontend:typescript": "tsc --build tsconfig.renderer.json",
|
||||||
"dev:webpack": "webpack --config scripts/webpack.config.js --mode development -w",
|
"dev:frontend:typescript": "tsc --build tsconfig.renderer.json -w --pretty --preserveWatchOutput",
|
||||||
|
"build:frontend:bundle": "vite build -c scripts/vite.config.mjs",
|
||||||
|
"dev:frontend:bundle": "vite -c scripts/vite.config.mjs",
|
||||||
|
"build:frontend": "npm run build:frontend:typescript && npm run build:frontend:bundle",
|
||||||
|
"dev:frontend": "concurrently -c blue,red -n tsc,bundle \"npm run dev:frontend:typescript\" \"npm run dev:frontend:bundle\"",
|
||||||
"build:index": "node scripts/buildfile.js",
|
"build:index": "node scripts/buildfile.js",
|
||||||
"dev:index": "node scripts/buildfile.js --watch --dev",
|
"dev:index": "node scripts/buildfile.js --watch --dev",
|
||||||
"build:ts": "tsc",
|
"build:backend": "tsc --build tsconfig.json",
|
||||||
"dev:ts": "tsc -w --pretty --preserveWatchOutput",
|
"dev:backend": "tsc --build tsconfig.json -w --pretty --preserveWatchOutput",
|
||||||
"build": "concurrently -c green,yellow,cyan -n webpack,index,typescript \"npm run build:webpack\" \"npm run build:index\" \"npm run build:ts\"",
|
"build": "concurrently -c green,yellow,cyan -n frontend,index,backend \"npm run build:frontend\" \"npm run build:index\" \"npm run build:backend\"",
|
||||||
"dev": "concurrently -c green,yellow,cyan -n webpack,index,typescript \"npm run dev:webpack\" \"npm run dev:index\" \"npm run dev:ts\"",
|
"dev": "concurrently -c green,yellow,cyan -n frontend,index,backend \"npm run dev:frontend\" \"npm run dev:index\" \"npm run dev:backend\"",
|
||||||
"test:fast": "electron-mocha --config .mocharc.json --grep \"@slow\" --invert",
|
"test:fast": "electron-mocha --config .mocharc.json --grep \"@slow\" --invert",
|
||||||
"test": "electron-mocha --config .mocharc.json",
|
"test": "electron-mocha --config .mocharc.json",
|
||||||
"coverage": "nyc npm run test",
|
"coverage": "nyc npm run test",
|
||||||
|
@ -61,6 +66,7 @@
|
||||||
"@electron-forge/cli": "^6.0.0-beta.55",
|
"@electron-forge/cli": "^6.0.0-beta.55",
|
||||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.55",
|
"@electron-forge/maker-squirrel": "^6.0.0-beta.55",
|
||||||
"@prettier/plugin-xml": "^0.13.1",
|
"@prettier/plugin-xml": "^0.13.1",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.12",
|
||||||
"@types/better-sqlite3": "^5.4.1",
|
"@types/better-sqlite3": "^5.4.1",
|
||||||
"@types/chai": "^4.2.18",
|
"@types/chai": "^4.2.18",
|
||||||
"@types/chai-fs": "^2.0.2",
|
"@types/chai-fs": "^2.0.2",
|
||||||
|
@ -72,7 +78,6 @@
|
||||||
"@types/node": "^14.17.0",
|
"@types/node": "^14.17.0",
|
||||||
"@types/sinon": "^10.0.0",
|
"@types/sinon": "^10.0.0",
|
||||||
"@types/uuid": "^8.3.0",
|
"@types/uuid": "^8.3.0",
|
||||||
"@types/webpack": "^5.28.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^4.24.0",
|
"@typescript-eslint/eslint-plugin": "^4.24.0",
|
||||||
"@typescript-eslint/parser": "^4.24.0",
|
"@typescript-eslint/parser": "^4.24.0",
|
||||||
"chai": "^4.3.4",
|
"chai": "^4.3.4",
|
||||||
|
@ -100,11 +105,8 @@
|
||||||
"prettier-plugin-svelte": "^2.3.0",
|
"prettier-plugin-svelte": "^2.3.0",
|
||||||
"sinon": "^11.1.1",
|
"sinon": "^11.1.1",
|
||||||
"svelte": "^3.38.2",
|
"svelte": "^3.38.2",
|
||||||
"svelte-loader": "^3.1.1",
|
|
||||||
"ts-loader": "^9.2.0",
|
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.2.4",
|
||||||
"webpack": "^5.37.1",
|
"vite": "^2.4.1"
|
||||||
"webpack-cli": "^4.7.0"
|
|
||||||
},
|
},
|
||||||
"repository": "https://git.fuwafuwa.moe/Xymorot/RenaiApp",
|
"repository": "https://git.fuwafuwa.moe/Xymorot/RenaiApp",
|
||||||
"bugs": "https://git.fuwafuwa.moe/Xymorot/RenaiApp/issues",
|
"bugs": "https://git.fuwafuwa.moe/Xymorot/RenaiApp/issues",
|
||||||
|
|
|
@ -4,15 +4,14 @@ const { watch } = require('chokidar');
|
||||||
const { debounce } = require('lodash');
|
const { debounce } = require('lodash');
|
||||||
const minimist = require('minimist');
|
const minimist = require('minimist');
|
||||||
const templating = require('../templates');
|
const templating = require('../templates');
|
||||||
const webpackConfig = require('./webpack.config');
|
|
||||||
|
|
||||||
/** @var {Object} argv */
|
/** @var {Object} argv */
|
||||||
const argv = minimist(process.argv);
|
const argv = minimist(process.argv);
|
||||||
|
|
||||||
function buildIndexHtml() {
|
function buildIndexHtml() {
|
||||||
const html = templating.compile(!!argv.dev);
|
const html = templating.compile();
|
||||||
|
|
||||||
fs.writeFileSync(path.resolve(webpackConfig.output.path, 'index.html'), html);
|
fs.writeFileSync(path.resolve('index.html'), html);
|
||||||
console.log('compiled index.html');
|
console.log('compiled index.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ const debouncedBuildIndexHtml = debounce(buildIndexHtml, 100, { leading: true, t
|
||||||
function build() {
|
function build() {
|
||||||
if (argv.watch) {
|
if (argv.watch) {
|
||||||
const templatesWatcher = watch('./templates/**/*');
|
const templatesWatcher = watch('./templates/**/*');
|
||||||
templatesWatcher.on('all', debouncedBuildIndexHtml);
|
templatesWatcher.on('all', () => debouncedBuildIndexHtml());
|
||||||
} else {
|
} else {
|
||||||
buildIndexHtml();
|
buildIndexHtml();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ const ignoreList = [
|
||||||
/^\/\.idea($|\/)/,
|
/^\/\.idea($|\/)/,
|
||||||
/^\/\.vscode($|\/)/,
|
/^\/\.vscode($|\/)/,
|
||||||
|
|
||||||
|
/^\/\.husky($|\/)/,
|
||||||
/^\/\.nyc_output($|\/)/,
|
/^\/\.nyc_output($|\/)/,
|
||||||
/^\/declarations($|\/)/,
|
/^\/declarations($|\/)/,
|
||||||
/^\/templates($|\/)/,
|
/^\/templates($|\/)/,
|
||||||
|
@ -21,18 +22,20 @@ const ignoreList = [
|
||||||
/^\/\.nycrc\.yml/,
|
/^\/\.nycrc\.yml/,
|
||||||
/^\/\.prettierrc\.yml/,
|
/^\/\.prettierrc\.yml/,
|
||||||
/^\/CONTRIBUTING\.md/,
|
/^\/CONTRIBUTING\.md/,
|
||||||
|
/^\/index\.html/,
|
||||||
/^\/ormconfig\.yml/,
|
/^\/ormconfig\.yml/,
|
||||||
/^\/package-lock\.json/,
|
/^\/package-lock\.json/,
|
||||||
/^\/tsconfig\.json/,
|
/^\/tsconfig\.json/,
|
||||||
/^\/tsconfig\.renderer\.json/,
|
/^\/tsconfig\.renderer\.json/,
|
||||||
|
|
||||||
/^\/node_modules\/\.cache($|\/)/,
|
/^\/node_modules\/\.cache($|\/)/,
|
||||||
|
/^\/node_modules\/\.vite($|\/)/,
|
||||||
// test and mock files:
|
// test and mock files:
|
||||||
/^.*\.(spec|mock)\.(ts|js(\.map)?)/,
|
/^.*\.(spec|mock)\.(ts|js(\.map)?)/,
|
||||||
/^\/src\/.*\/test\/.*$/,
|
/^\/src\/.*\/test\/.*$/,
|
||||||
// original typescript source and generated source map files:
|
// original source and generated source map files:
|
||||||
/^\/src\/.*\.(ts|js\.map)/,
|
/^\/src\/.*\.(ts|js\.map)/,
|
||||||
/^\/src\/.*\.eslintrc\.json/,
|
/^\/src\/(renderer($|\/)|renderer\.js)/,
|
||||||
];
|
];
|
||||||
|
|
||||||
const name = packageJson.productName;
|
const name = packageJson.productName;
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* @type {import('rollup').RollupOptions}
|
||||||
|
*/
|
||||||
|
const options = {};
|
||||||
|
|
||||||
|
export default options;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
import rollupOptions from './rollup.config.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('vite').UserConfig}
|
||||||
|
*/
|
||||||
|
const config = {
|
||||||
|
plugins: [svelte()],
|
||||||
|
base: '',
|
||||||
|
build: {
|
||||||
|
target: 'chrome91',
|
||||||
|
outDir: 'frontend',
|
||||||
|
rollupOptions,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
|
@ -1,29 +0,0 @@
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
mode: 'production',
|
|
||||||
entry: path.resolve(__dirname, '../src/renderer.ts'),
|
|
||||||
target: 'electron13-renderer',
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, '../frontend'),
|
|
||||||
filename: 'bundle.js',
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.svelte$/,
|
|
||||||
loader: 'svelte-loader',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ts$/,
|
|
||||||
loader: 'ts-loader',
|
|
||||||
options: {
|
|
||||||
configFile: path.resolve('tsconfig.renderer.json'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.ts', '.js'],
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -2,6 +2,7 @@ import 'reflect-metadata';
|
||||||
import { Container, interfaces } from 'inversify';
|
import { Container, interfaces } from 'inversify';
|
||||||
import type { AppWindowInterface } from '../modules/app-window/app-window-interface';
|
import type { AppWindowInterface } from '../modules/app-window/app-window-interface';
|
||||||
import { MainAppWindow } from '../modules/app-window/main-app-window';
|
import { MainAppWindow } from '../modules/app-window/main-app-window';
|
||||||
|
import { MainAppWindow as MainAppWindowDev } from '../modules/app-window/main-app-window.dev';
|
||||||
import { Dialog } from '../modules/dialog/dialog';
|
import { Dialog } from '../modules/dialog/dialog';
|
||||||
import type { DialogInterface } from '../modules/dialog/dialog-interface';
|
import type { DialogInterface } from '../modules/dialog/dialog-interface';
|
||||||
import { Logger } from '../modules/logger/logger';
|
import { Logger } from '../modules/logger/logger';
|
||||||
|
@ -40,6 +41,7 @@ import { WorldNameSerializer } from '../modules/serialization/serializers/world-
|
||||||
import { WorldSerializer } from '../modules/serialization/serializers/world-serializer';
|
import { WorldSerializer } from '../modules/serialization/serializers/world-serializer';
|
||||||
import type { SourceGetterInterface } from '../modules/source/source-getter-interface';
|
import type { SourceGetterInterface } from '../modules/source/source-getter-interface';
|
||||||
import { Store } from '../modules/store/store';
|
import { Store } from '../modules/store/store';
|
||||||
|
import { isDev } from './env';
|
||||||
import BindingToSyntax = interfaces.BindingToSyntax;
|
import BindingToSyntax = interfaces.BindingToSyntax;
|
||||||
|
|
||||||
export const enum Service {
|
export const enum Service {
|
||||||
|
@ -182,4 +184,8 @@ container.bind(Service.NHENTAI_APP_WINDOW).to(NhentaiAppWindow);
|
||||||
container.bind(Service.NHENTAI_API).to(NhentaiApi);
|
container.bind(Service.NHENTAI_API).to(NhentaiApi);
|
||||||
container.bind(Service.NHENTAI_SOURCE_GETTER).to(NhentaiSourceGetter);
|
container.bind(Service.NHENTAI_SOURCE_GETTER).to(NhentaiSourceGetter);
|
||||||
|
|
||||||
container.bind(Service.APP_WINDOW_MAIN).to(MainAppWindow);
|
if (isDev()) {
|
||||||
|
container.bind(Service.APP_WINDOW_MAIN).to(MainAppWindowDev);
|
||||||
|
} else {
|
||||||
|
container.bind(Service.APP_WINDOW_MAIN).to(MainAppWindow);
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { app, BrowserWindow, Event, LoadFileOptions, LoadURLOptions, NewWindowWe
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { isDev } from '../../core/env';
|
import { isDev } from '../../core/env';
|
||||||
import { completeContentSecurityPolicy, setWindowCsp } from '../session/session-util';
|
import { setWindowCsp } from '../session/session-util';
|
||||||
import type { AppWindowInterface } from './app-window-interface';
|
import type { AppWindowInterface } from './app-window-interface';
|
||||||
import { WindowClosedError } from './window-closed-error';
|
import { WindowClosedError } from './window-closed-error';
|
||||||
import BrowserWindowConstructorOptions = Electron.BrowserWindowConstructorOptions;
|
import BrowserWindowConstructorOptions = Electron.BrowserWindowConstructorOptions;
|
||||||
|
@ -64,7 +64,7 @@ export abstract class AppWindow implements AppWindowInterface {
|
||||||
public open(): Promise<void> {
|
public open(): Promise<void> {
|
||||||
this._window = new BrowserWindow(this.options);
|
this._window = new BrowserWindow(this.options);
|
||||||
|
|
||||||
setWindowCsp(this._window, completeContentSecurityPolicy(this.getCsp()));
|
setWindowCsp(this._window, this.getCsp());
|
||||||
|
|
||||||
this._window.on('closed', () => {
|
this._window.on('closed', () => {
|
||||||
this.onClosed();
|
this.onClosed();
|
||||||
|
|
|
@ -17,4 +17,10 @@ export abstract class FileAppWindow extends AppWindow {
|
||||||
protected load(window: BrowserWindow): Promise<void> {
|
protected load(window: BrowserWindow): Promise<void> {
|
||||||
return window.loadFile(this.uri, this.loadOptions);
|
return window.loadFile(this.uri, this.loadOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getCsp(): Session.ContentSecurityPolicy {
|
||||||
|
return {
|
||||||
|
'default-src': ['file:'],
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import path from 'path';
|
||||||
|
import { injectable } from 'inversify';
|
||||||
|
import { Service } from '../../core/container';
|
||||||
|
import { inject } from '../../core/inject';
|
||||||
|
import { mergeContentSecurityPolicy } from '../session/session-util';
|
||||||
|
import { UrlAppWindow } from './url-app-window';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MainAppWindow extends UrlAppWindow {
|
||||||
|
public constructor(@inject(Service.LOGGER) logger: LoggerInterface) {
|
||||||
|
super(logger, 'http://localhost:3000', {
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.resolve('src/preload.js'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCsp(): Session.ContentSecurityPolicy {
|
||||||
|
return mergeContentSecurityPolicy(super.getCsp(), {
|
||||||
|
'script-src-elem': ["'self'"],
|
||||||
|
'connect-src': ['ws:'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import path from 'path';
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { Service } from '../../core/container';
|
import { Service } from '../../core/container';
|
||||||
import { inject } from '../../core/inject';
|
import { inject } from '../../core/inject';
|
||||||
|
@ -8,8 +9,7 @@ export class MainAppWindow extends FileAppWindow {
|
||||||
public constructor(@inject(Service.LOGGER) logger: LoggerInterface) {
|
public constructor(@inject(Service.LOGGER) logger: LoggerInterface) {
|
||||||
super(logger, 'frontend/index.html', {
|
super(logger, 'frontend/index.html', {
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
preload: path.resolve('src/preload.js'),
|
||||||
contextIsolation: false,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { dialog, OpenDialogOptions } from 'electron';
|
import { dialog, OpenDialogOptions } from 'electron';
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { t } from '../../../shared/services/translation/t';
|
import { t } from '../translation/t';
|
||||||
import type { DialogInterface } from './dialog-interface';
|
import type { DialogInterface } from './dialog-interface';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { createWriteStream } from 'fs-extra';
|
import { createWriteStream } from 'fs-extra';
|
||||||
import { t } from '../../../shared/services/translation/t';
|
|
||||||
import { container, Service } from '../../core/container';
|
import { container, Service } from '../../core/container';
|
||||||
import { ipcServer } from '../ipc/ipc-server';
|
import { ipcServer } from '../ipc/ipc-server';
|
||||||
|
import { t } from '../translation/t';
|
||||||
|
|
||||||
ipcServer.answer(IpcChannel.NHENTAI_SAVE_FAVORITES, async (): Promise<void> => {
|
ipcServer.answer(IpcChannel.NHENTAI_SAVE_FAVORITES, async (): Promise<void> => {
|
||||||
const nhentaiApi = container.get(Service.NHENTAI_API);
|
const nhentaiApi = container.get(Service.NHENTAI_API);
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { isDev } from '../../core/env';
|
import { isDev } from '../../core/env';
|
||||||
import ContentSecurityPolicy = Session.ContentSecurityPolicy;
|
import ContentSecurityPolicy = Session.ContentSecurityPolicy;
|
||||||
import CompleteContentSecurityPolicy = Session.CompleteContentSecurityPolicy;
|
|
||||||
|
|
||||||
const defaultCsp: Session.ContentSecurityPolicy = {
|
const defaultCsp: Session.ContentSecurityPolicy = {
|
||||||
'default-src': ["'self'"],
|
'default-src': ["'self'"],
|
||||||
'style-src': ["'unsafe-inline'"],
|
'style-src-attr': ["'unsafe-inline'"],
|
||||||
};
|
};
|
||||||
|
|
||||||
function stringifyCspHeader(csp: ContentSecurityPolicy): string {
|
function stringifyCspHeader(csp: ContentSecurityPolicy): string {
|
||||||
|
@ -30,7 +29,7 @@ export function mergeContentSecurityPolicy(...contentSecurityPolicies: ContentSe
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWindowCsp(window: Electron.BrowserWindow, csp: CompleteContentSecurityPolicy): void {
|
export function setWindowCsp(window: Electron.BrowserWindow, csp: ContentSecurityPolicy): void {
|
||||||
const mergedCsp: ContentSecurityPolicy = { ...defaultCsp, ...csp };
|
const mergedCsp: ContentSecurityPolicy = { ...defaultCsp, ...csp };
|
||||||
|
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
|
@ -52,27 +51,3 @@ export function setWindowCsp(window: Electron.BrowserWindow, csp: CompleteConten
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* fills script-src-elem and script-src-attr with script-src when not present,
|
|
||||||
* fills rest of missing policies with default-src or nothing when it is not present
|
|
||||||
*/
|
|
||||||
export function completeContentSecurityPolicy(csp: ContentSecurityPolicy): CompleteContentSecurityPolicy {
|
|
||||||
const defaultSrc = csp['default-src'] ?? [];
|
|
||||||
const scriptSrc = csp['script-src'] ?? [];
|
|
||||||
return {
|
|
||||||
'child-src': csp['child-src'] ?? defaultSrc,
|
|
||||||
'connect-src': csp['connect-src'] ?? defaultSrc,
|
|
||||||
'default-src': csp['default-src'] ?? defaultSrc,
|
|
||||||
'font-src': csp['font-src'] ?? defaultSrc,
|
|
||||||
'frame-src': csp['frame-src'] ?? defaultSrc,
|
|
||||||
'img-src': csp['img-src'] ?? defaultSrc,
|
|
||||||
'media-src': csp['media-src'] ?? defaultSrc,
|
|
||||||
'object-src': ["'none'"],
|
|
||||||
'script-src': csp['script-src'] ?? defaultSrc,
|
|
||||||
'script-src-elem': csp['script-src-elem'] ?? scriptSrc ?? defaultSrc,
|
|
||||||
'script-src-attr': csp['script-src-attr'] ?? scriptSrc ?? defaultSrc,
|
|
||||||
'style-src': csp['style-src'] ?? defaultSrc,
|
|
||||||
'worker-src': csp['worker-src'] ?? defaultSrc,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,25 +19,8 @@ declare namespace Session {
|
||||||
'script-src-elem'?: CspValue[];
|
'script-src-elem'?: CspValue[];
|
||||||
'script-src-attr'?: CspValue[];
|
'script-src-attr'?: CspValue[];
|
||||||
'style-src'?: CspValue[];
|
'style-src'?: CspValue[];
|
||||||
|
'style-src-elem'?: CspValue[];
|
||||||
|
'style-src-attr'?: CspValue[];
|
||||||
'worker-src'?: CspValue[];
|
'worker-src'?: CspValue[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* meant for usages where the browser shouldn't unexpectedly fall back to default-src
|
|
||||||
*/
|
|
||||||
type CompleteContentSecurityPolicy = {
|
|
||||||
'child-src': CspValue[];
|
|
||||||
'connect-src': CspValue[];
|
|
||||||
'default-src': CspValue[];
|
|
||||||
'font-src': CspValue[];
|
|
||||||
'frame-src': CspValue[];
|
|
||||||
'img-src': CspValue[];
|
|
||||||
'media-src': CspValue[];
|
|
||||||
'object-src': ["'none'"];
|
|
||||||
'script-src': CspValue[];
|
|
||||||
'script-src-elem': CspValue[];
|
|
||||||
'script-src-attr': CspValue[];
|
|
||||||
'style-src': CspValue[];
|
|
||||||
'worker-src': CspValue[];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
// same as in renderer
|
||||||
|
|
||||||
|
import { IntlMessageFormat } from 'intl-messageformat';
|
||||||
|
import * as en from '../../../shared/translation/en.json';
|
||||||
|
|
||||||
|
const localeData = {
|
||||||
|
[Locale.EN]: en as TranslationData,
|
||||||
|
};
|
||||||
|
|
||||||
|
function getDeep(deepKey: string, data: TranslationData): string {
|
||||||
|
const keyChain = deepKey.split('.');
|
||||||
|
if (!keyChain.length) {
|
||||||
|
return deepKey;
|
||||||
|
}
|
||||||
|
let index = 0;
|
||||||
|
let curr: TranslationData | string = data;
|
||||||
|
do {
|
||||||
|
const key = keyChain[index];
|
||||||
|
curr = curr[key];
|
||||||
|
index++;
|
||||||
|
} while (typeof curr !== 'string' && index < keyChain.length);
|
||||||
|
if (typeof curr === 'string') {
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
return deepKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const intlMessageFormatCache: Record<Locale, Record<string, IntlMessageFormat>> = {
|
||||||
|
[Locale.EN]: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* translates a message
|
||||||
|
*
|
||||||
|
* @param key - a deep key of the format "labels.actions.save"
|
||||||
|
* @param values - arguments for the message
|
||||||
|
* @param locale - see {@link Locale} for implemented locales
|
||||||
|
*/
|
||||||
|
export function t(key: string, values?: Record<string, string | number | boolean>, locale: Locale = Locale.EN): string {
|
||||||
|
if (!intlMessageFormatCache[locale][key]) {
|
||||||
|
const message = getDeep(key, localeData[locale]);
|
||||||
|
intlMessageFormatCache[locale][key] = new IntlMessageFormat(message, locale);
|
||||||
|
}
|
||||||
|
const intlMessageFormat = intlMessageFormatCache[locale][key];
|
||||||
|
const formatted = intlMessageFormat.format(values);
|
||||||
|
if (typeof formatted === 'string') {
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer);
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { t } from '../../../shared/services/translation/t';
|
|
||||||
import { nhentaiGetWork } from '../../services/api';
|
import { nhentaiGetWork } from '../../services/api';
|
||||||
|
import { t } from '../../services/translation/t';
|
||||||
import Work from '../content/Work.svelte';
|
import Work from '../content/Work.svelte';
|
||||||
import SvelteButton from '../elements/SvelteButton.svelte';
|
import SvelteButton from '../elements/SvelteButton.svelte';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { t } from '../../../shared/services/translation/t';
|
|
||||||
import { nhentaiSaveFavorites } from '../../services/api';
|
import { nhentaiSaveFavorites } from '../../services/api';
|
||||||
|
import { t } from '../../services/translation/t';
|
||||||
import SvelteButton from '../elements/SvelteButton.svelte';
|
import SvelteButton from '../elements/SvelteButton.svelte';
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import { ipcRenderer } from 'electron';
|
import type { IpcRenderer } from 'electron';
|
||||||
|
|
||||||
|
// @ts-ignore -- https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||||
|
const ipcRenderer: IpcRenderer = window.ipcRenderer as IpcRenderer;
|
||||||
|
|
||||||
export const ipcClient: IpcClient = {
|
export const ipcClient: IpcClient = {
|
||||||
ask<T extends IpcChannel>(channel: T, data?: IpcParameter<T>): Promise<IpcAnswer<T>> {
|
ask<T extends IpcChannel>(channel: T, data?: IpcParameter<T>): Promise<IpcAnswer<T>> {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { IntlMessageFormat } from 'intl-messageformat';
|
// same as in main
|
||||||
import { en } from './en';
|
|
||||||
|
|
||||||
const enum Locale {
|
import { IntlMessageFormat } from 'intl-messageformat';
|
||||||
EN = 'en',
|
import * as en from '../../../shared/translation/en.json';
|
||||||
}
|
|
||||||
|
|
||||||
const localeData = {
|
const localeData = {
|
||||||
[Locale.EN]: en,
|
[Locale.EN]: en as TranslationData,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getDeep(deepKey: string, data: TranslationData): string {
|
function getDeep(deepKey: string, data: TranslationData): string {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue