mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
build: create app protocol
- add a build script (wip not yet packaged)
This commit is contained in:
parent
dde007dd09
commit
8df735d76f
79
build/scripts/build.mjs
Normal file
79
build/scripts/build.mjs
Normal file
@ -0,0 +1,79 @@
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import esbuild from 'esbuild';
|
||||
import { $ } from 'execa';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import * as vite from 'vite';
|
||||
import { getMainProcessCommonConfig } from './helpers.mjs';
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.join(dirname, '..', '..');
|
||||
|
||||
const $$ = $({ stdio: 'inherit' });
|
||||
await $$`rm -rf ${path.join(root, 'dist_electron', 'build')}`;
|
||||
|
||||
await buildMainProcessSource();
|
||||
await buildRendererProcessSource();
|
||||
|
||||
async function buildMainProcessSource() {
|
||||
const commonConfig = getMainProcessCommonConfig(root);
|
||||
const result = await esbuild.build({
|
||||
...commonConfig,
|
||||
outfile: path.join(root, 'dist_electron', 'build', 'main.js'),
|
||||
});
|
||||
|
||||
if (result.errors.length) {
|
||||
console.error('app build failed due to main process source build');
|
||||
result.errors.forEach((err) => console.error(err));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function buildRendererProcessSource() {
|
||||
const base = 'app://';
|
||||
const outDir = path.join(root, 'dist_electron', 'build', 'src');
|
||||
await vite.build({
|
||||
base: `/${base}`,
|
||||
root: path.join(root, 'src'),
|
||||
build: { outDir },
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
vue: 'vue/dist/vue.esm-bundler.js',
|
||||
fyo: path.join(root, 'fyo'),
|
||||
src: path.join(root, 'src'),
|
||||
schemas: path.join(root, 'schemas'),
|
||||
backend: path.join(root, 'backend'),
|
||||
models: path.join(root, 'models'),
|
||||
utils: path.join(root, 'utils'),
|
||||
regional: path.join(root, 'regional'),
|
||||
reports: path.join(root, 'reports'),
|
||||
dummy: path.join(root, 'dummy'),
|
||||
fixtures: path.join(root, 'fixtures'),
|
||||
},
|
||||
},
|
||||
});
|
||||
removeBaseLeadingSlash(outDir, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes leading slash from all renderer files
|
||||
* electron uses a custom registered protocol to load the
|
||||
* files: "app://"
|
||||
*
|
||||
* @param {string} dir
|
||||
* @param {string} base
|
||||
*/
|
||||
function removeBaseLeadingSlash(dir, base) {
|
||||
for (const file of fs.readdirSync(dir)) {
|
||||
const filePath = path.join(dir, file);
|
||||
if (fs.lstatSync(filePath).isDirectory()) {
|
||||
removeBaseLeadingSlash(filePath, base);
|
||||
continue;
|
||||
}
|
||||
|
||||
const contents = fs.readFileSync(filePath).toString('utf-8');
|
||||
fs.writeFileSync(filePath, contents.replaceAll('/' + base, base));
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import esbuild from 'esbuild';
|
||||
import { $ } from 'execa';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { excludeVendorFromSourceMap } from './plugins.mjs';
|
||||
import { getMainProcessCommonConfig } from './helpers.mjs';
|
||||
|
||||
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
|
||||
process.env['NODE_ENV'] = 'development';
|
||||
@ -45,16 +45,8 @@ if (!process.argv.includes('--no-renderer')) {
|
||||
* to [re]build the main process code
|
||||
*/
|
||||
const ctx = await esbuild.context({
|
||||
entryPoints: [path.join(root, 'main.ts')],
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
sourcesContent: false,
|
||||
platform: 'node',
|
||||
target: 'node16',
|
||||
outfile: path.join(root, 'dist', 'dev', 'main.js'),
|
||||
external: ['knex', 'electron', 'better-sqlite3'],
|
||||
plugins: [excludeVendorFromSourceMap],
|
||||
write: true,
|
||||
...getMainProcessCommonConfig(root),
|
||||
outfile: path.join(root, 'dist_electron', 'dev', 'main.js'),
|
||||
});
|
||||
|
||||
/**
|
||||
@ -135,7 +127,7 @@ async function handleResult(result) {
|
||||
function runElectron() {
|
||||
const electronProcess = $$`npx electron --inspect=5858 ${path.join(
|
||||
root,
|
||||
'dist',
|
||||
'dist_electron',
|
||||
'dev',
|
||||
'main.js'
|
||||
)}`;
|
||||
|
51
build/scripts/helpers.mjs
Normal file
51
build/scripts/helpers.mjs
Normal file
@ -0,0 +1,51 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* Common ESBuild config used for building main process source
|
||||
* code for both dev and production.
|
||||
*
|
||||
* @param {string} root
|
||||
* @returns {import('esbuild').BuildOptions}
|
||||
*/
|
||||
export function getMainProcessCommonConfig(root) {
|
||||
return {
|
||||
entryPoints: [path.join(root, 'main.ts')],
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
sourcesContent: false,
|
||||
platform: 'node',
|
||||
target: 'node16',
|
||||
external: ['knex', 'electron', 'better-sqlite3'],
|
||||
plugins: [excludeVendorFromSourceMap],
|
||||
write: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ESBuild plugin used to prevent source maps from being generated for
|
||||
* packages inside node_modules, only first-party code source maps
|
||||
* are to be included.
|
||||
*
|
||||
* Note, this is used only for the main process source code.
|
||||
*
|
||||
* source: https://github.com/evanw/esbuild/issues/1685#issuecomment-944916409
|
||||
* @type {import('esbuild').Plugin}
|
||||
*/
|
||||
export const excludeVendorFromSourceMap = {
|
||||
name: 'excludeVendorFromSourceMap',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /node_modules/ }, (args) => {
|
||||
if (args.path.endsWith('.json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
contents:
|
||||
fs.readFileSync(args.path, 'utf8') +
|
||||
'\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJtYXBwaW5ncyI6IkEifQ==',
|
||||
loader: 'default',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
@ -1,23 +0,0 @@
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
* source: https://github.com/evanw/esbuild/issues/1685#issuecomment-944916409
|
||||
* @type {import('esbuild').Plugin}
|
||||
*/
|
||||
export const excludeVendorFromSourceMap = {
|
||||
name: 'excludeVendorFromSourceMap',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /node_modules/ }, (args) => {
|
||||
if (args.path.endsWith('.json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
contents:
|
||||
fs.readFileSync(args.path, 'utf8') +
|
||||
'\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJtYXBwaW5ncyI6IkEifQ==',
|
||||
loader: 'default',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
61
main.ts
61
main.ts
@ -8,9 +8,12 @@ import {
|
||||
BrowserWindow,
|
||||
BrowserWindowConstructorOptions,
|
||||
protocol,
|
||||
ProtocolRequest,
|
||||
ProtocolResponse,
|
||||
} from 'electron';
|
||||
import Store from 'electron-store';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import registerAppLifecycleListeners from './main/registerAppLifecycleListeners';
|
||||
import registerAutoUpdaterListeners from './main/registerAutoUpdaterListeners';
|
||||
@ -122,15 +125,20 @@ export class Main {
|
||||
this.mainWindow = new BrowserWindow(options);
|
||||
|
||||
if (this.isDevelopment) {
|
||||
this.loadDevServerURL();
|
||||
this.setViteServerURL();
|
||||
} else {
|
||||
this.loadAppUrl();
|
||||
this.registerAppProtocol();
|
||||
}
|
||||
|
||||
this.mainWindow!.loadURL(this.winURL);
|
||||
if (this.isDevelopment && !this.isTest) {
|
||||
this.mainWindow!.webContents.openDevTools();
|
||||
}
|
||||
|
||||
this.setMainWindowListeners();
|
||||
}
|
||||
|
||||
loadDevServerURL() {
|
||||
setViteServerURL() {
|
||||
let port = 6969;
|
||||
let host = '0.0.0.0';
|
||||
|
||||
@ -141,18 +149,13 @@ export class Main {
|
||||
|
||||
// Load the url of the dev server if in development mode
|
||||
this.winURL = `http://${host}:${port}/`;
|
||||
this.mainWindow!.loadURL(this.winURL);
|
||||
|
||||
if (this.isDevelopment && !this.isTest) {
|
||||
this.mainWindow!.webContents.openDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
loadAppUrl() {
|
||||
// createProtocol('app');
|
||||
// Load the index.html when not in development
|
||||
// this.winURL = 'app://./index.html';
|
||||
// this.mainWindow!.loadURL(this.winURL);
|
||||
registerAppProtocol() {
|
||||
protocol.registerBufferProtocol('app', bufferProtocolCallback);
|
||||
|
||||
// Use the registered protocol url to load the files.
|
||||
this.winURL = 'app://./index.html';
|
||||
}
|
||||
|
||||
setMainWindowListeners() {
|
||||
@ -170,4 +173,36 @@ export class Main {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to register the custom app protocol,
|
||||
* during prod, files are read and served by using this
|
||||
* protocol.
|
||||
*/
|
||||
function bufferProtocolCallback(
|
||||
request: ProtocolRequest,
|
||||
callback: (response: ProtocolResponse) => void
|
||||
) {
|
||||
const { pathname, host } = new URL(request.url);
|
||||
const filePath = path.join(
|
||||
__dirname,
|
||||
'src',
|
||||
decodeURI(host),
|
||||
decodeURI(pathname)
|
||||
);
|
||||
|
||||
fs.readFile(filePath, (_, data) => {
|
||||
const extension = path.extname(filePath).toLowerCase();
|
||||
const mimeType =
|
||||
{
|
||||
'.js': 'text/javascript',
|
||||
'.css': 'text/css',
|
||||
'.html': 'text/html',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.json': 'application/json',
|
||||
}[extension] ?? '';
|
||||
|
||||
callback({ mimeType, data });
|
||||
});
|
||||
}
|
||||
|
||||
export default new Main();
|
||||
|
@ -29,7 +29,7 @@ async function getPrintTemplatePaths(): Promise<{
|
||||
const files = await fs.readdir(root);
|
||||
return { files, root };
|
||||
} catch {
|
||||
root = path.join(__dirname, `../templates`);
|
||||
root = path.join(__dirname, '..', '..', `templates`);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -8,15 +8,13 @@
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "node build/scripts/dev.mjs",
|
||||
"build": "node build/scripts/build.mjs",
|
||||
"dev:main": "node build/scripts/dev.mjs --no-renderer",
|
||||
"dev:renderer": "yarn vite",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"release": "scripts/publish-mac-arm.sh",
|
||||
"postinstall": "electron-rebuild",
|
||||
"postuninstall": "electron-rebuild",
|
||||
"electron:build": "vue-cli-service electron:build",
|
||||
"electron:serve": "vue-cli-service electron:serve",
|
||||
"script:translate": "scripts/runner.sh scripts/generateTranslations.ts",
|
||||
"script:profile": "scripts/profile.sh",
|
||||
"test": "scripts/test.sh"
|
||||
@ -73,6 +71,7 @@
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^7.0.0",
|
||||
"execa": "^7.1.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lint-staged": "^11.2.6",
|
||||
"postcss": "^8",
|
||||
"prettier": "^2.4.1",
|
||||
|
@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>Frappe Books</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but Frappe Books doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<!-- built files will replace body.innerHTML -->
|
||||
</body>
|
||||
</html>
|
@ -2,11 +2,15 @@ import vue from '@vitejs/plugin-vue';
|
||||
import path from 'path';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
/**
|
||||
* This is a work in progress vite config. Currently only dev works.
|
||||
*/
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
/**
|
||||
* This vite config file is used only for dev mode, i.e.
|
||||
* to create a serve build modules of the source code
|
||||
* which will be rendered by electron.
|
||||
*
|
||||
* For building the project, vite is used programmatically
|
||||
* see build/scripts/build.mjs for this.
|
||||
*/
|
||||
export default () => {
|
||||
let port = 6969;
|
||||
let host = '0.0.0.0';
|
||||
|
@ -6534,6 +6534,15 @@ fs-extra@^10.1.0:
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^11.1.1:
|
||||
version "11.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d"
|
||||
integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
|
Loading…
Reference in New Issue
Block a user