mirror of
https://github.com/LmeSzinc/AzurLaneAutoScript.git
synced 2025-01-07 07:47:07 +08:00
Add: Vite Electron Builder template
From https://github.com/cawa-93/vite-electron-builder
This commit is contained in:
parent
f66803b1ad
commit
a4e4dda9f0
18
webapp/.editorconfig
Normal file
18
webapp/.editorconfig
Normal file
@ -0,0 +1,18 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# https://github.com/jokeyrhyme/standard-editorconfig
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# defaults
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
0
webapp/.env.development
Normal file
0
webapp/.env.development
Normal file
54
webapp/.eslintrc.json
Normal file
54
webapp/.eslintrc.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true,
|
||||
"browser": false
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
/** @see https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#recommended-configs */
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"types/env.d.ts",
|
||||
"node_modules/**",
|
||||
"**/dist/**"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
|
||||
/**
|
||||
* Having a semicolon helps the optimizer interpret your code correctly.
|
||||
* This avoids rare errors in optimized code.
|
||||
* @see https://twitter.com/alex_kozack/status/1364210394328408066
|
||||
*/
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
/**
|
||||
* This will make the history of changes in the hit a little cleaner
|
||||
*/
|
||||
"comma-dangle": [
|
||||
"warn",
|
||||
"always-multiline"
|
||||
],
|
||||
/**
|
||||
* Just for beauty
|
||||
*/
|
||||
"quotes": [
|
||||
"warn", "single"
|
||||
]
|
||||
}
|
||||
}
|
3
webapp/.gitattributes
vendored
Normal file
3
webapp/.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.github/actions/**/*.js linguist-detectable=false
|
||||
scripts/*.js linguist-detectable=false
|
||||
*.config.js linguist-detectable=false
|
4
webapp/.github/FUNDING.yml
vendored
Normal file
4
webapp/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: Kozack
|
||||
open_collective: vite-electron-builder
|
27
webapp/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
webapp/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: cawa-93
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
5
webapp/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
webapp/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions & Discussions
|
||||
url: https://github.com/cawa-93/vite-electron-builder/discussions/categories/q-a
|
||||
about: Use GitHub discussions for message-board style questions and discussions.
|
20
webapp/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
webapp/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: cawa-93
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
23
webapp/.github/actions/release-notes/action.yml
vendored
Normal file
23
webapp/.github/actions/release-notes/action.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: 'Release Notes'
|
||||
description: 'Return release notes based on Git Commits'
|
||||
inputs:
|
||||
from:
|
||||
description: 'Commit from which start log'
|
||||
required: true
|
||||
to:
|
||||
description: 'Commit to which end log'
|
||||
required: true
|
||||
include-commit-body:
|
||||
description: 'Should the commit body be in notes'
|
||||
required: false
|
||||
default: 'false'
|
||||
include-abbreviated-commit:
|
||||
description: 'Should the commit sha be in notes'
|
||||
required: false
|
||||
default: 'true'
|
||||
outputs:
|
||||
release-note: # id of output
|
||||
description: 'Release notes'
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'main.js'
|
346
webapp/.github/actions/release-notes/main.js
vendored
Normal file
346
webapp/.github/actions/release-notes/main.js
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
// TODO: Refactor this action
|
||||
|
||||
const {execSync} = require('child_process');
|
||||
|
||||
/**
|
||||
* Gets the value of an input. The value is also trimmed.
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns string
|
||||
*/
|
||||
function getInput(name, options) {
|
||||
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
|
||||
return val.trim();
|
||||
}
|
||||
|
||||
const START_FROM = getInput('from');
|
||||
const END_TO = getInput('to');
|
||||
const INCLUDE_COMMIT_BODY = getInput('include-commit-body') === 'true';
|
||||
const INCLUDE_ABBREVIATED_COMMIT = getInput('include-abbreviated-commit') === 'true';
|
||||
|
||||
/**
|
||||
* @typedef {Object} ICommit
|
||||
* @property {string | undefined} abbreviated_commit
|
||||
* @property {string | undefined} subject
|
||||
* @property {string | undefined} body
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {ICommit & {type: string | undefined, scope: string | undefined}} ICommitExtended
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Any unique string that is guaranteed not to be used in committee text.
|
||||
* Used to split data in the commit line
|
||||
* @type {string}
|
||||
*/
|
||||
const commitInnerSeparator = '~~~~';
|
||||
|
||||
|
||||
/**
|
||||
* Any unique string that is guaranteed not to be used in committee text.
|
||||
* Used to split each commit line
|
||||
* @type {string}
|
||||
*/
|
||||
const commitOuterSeparator = '₴₴₴₴';
|
||||
|
||||
|
||||
/**
|
||||
* Commit data to be obtained.
|
||||
* @type {Map<string, string>}
|
||||
*
|
||||
* @see https://git-scm.com/docs/git-log#Documentation/git-log.txt-emnem
|
||||
*/
|
||||
const commitDataMap = new Map([
|
||||
['subject', '%s'], // Required
|
||||
]);
|
||||
|
||||
if (INCLUDE_COMMIT_BODY) {
|
||||
commitDataMap.set('body', '%b');
|
||||
}
|
||||
|
||||
if (INCLUDE_ABBREVIATED_COMMIT) {
|
||||
commitDataMap.set('abbreviated_commit', '%h');
|
||||
}
|
||||
|
||||
/**
|
||||
* The type used to group commits that do not comply with the convention
|
||||
* @type {string}
|
||||
*/
|
||||
const fallbackType = 'other';
|
||||
|
||||
|
||||
/**
|
||||
* List of all desired commit groups and in what order to display them.
|
||||
* @type {string[]}
|
||||
*/
|
||||
const supportedTypes = [
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'refactor',
|
||||
'style',
|
||||
'docs',
|
||||
'test',
|
||||
'build',
|
||||
'ci',
|
||||
'chore',
|
||||
'revert',
|
||||
'deps',
|
||||
fallbackType,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {string} commitString
|
||||
* @returns {ICommit}
|
||||
*/
|
||||
function parseCommit(commitString) {
|
||||
/** @type {ICommit} */
|
||||
const commitDataObj = {};
|
||||
const commitDataArray =
|
||||
commitString
|
||||
.split(commitInnerSeparator)
|
||||
.map(s => s.trim());
|
||||
|
||||
for (const [key] of commitDataMap) {
|
||||
commitDataObj[key] = commitDataArray.shift();
|
||||
}
|
||||
|
||||
return commitDataObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of commits since the last git tag
|
||||
* @return {ICommit[]}
|
||||
*/
|
||||
function getCommits() {
|
||||
|
||||
const format = Array.from(commitDataMap.values()).join(commitInnerSeparator) + commitOuterSeparator;
|
||||
|
||||
const logs = String(execSync(`git --no-pager log ${START_FROM}..${END_TO} --pretty=format:"${format}" --reverse`));
|
||||
|
||||
return logs
|
||||
.trim()
|
||||
.split(commitOuterSeparator)
|
||||
.filter(r => !!r.trim()) // Skip empty lines
|
||||
.map(parseCommit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ICommit} commit
|
||||
* @return {ICommitExtended}
|
||||
*/
|
||||
function setCommitTypeAndScope(commit) {
|
||||
|
||||
const matchRE = new RegExp(`^(?:(${supportedTypes.join('|')})(?:\\((\\S+)\\))?:)?(.*)`, 'i');
|
||||
|
||||
let [, type, scope, clearSubject] = commit.subject.match(matchRE);
|
||||
|
||||
/**
|
||||
* Additional rules for checking committees that do not comply with the convention, but for which it is possible to determine the type.
|
||||
*/
|
||||
// Commits like `revert something`
|
||||
if (type === undefined && commit.subject.startsWith('revert')) {
|
||||
type = 'revert';
|
||||
}
|
||||
|
||||
return {
|
||||
...commit,
|
||||
type: (type || fallbackType).toLowerCase().trim(),
|
||||
scope: (scope || '').toLowerCase().trim(),
|
||||
subject: (clearSubject || commit.subject).trim(),
|
||||
};
|
||||
}
|
||||
|
||||
class CommitGroup {
|
||||
constructor() {
|
||||
this.scopes = new Map;
|
||||
this.commits = [];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ICommitExtended[]} array
|
||||
* @param {ICommitExtended} commit
|
||||
*/
|
||||
static _pushOrMerge(array, commit) {
|
||||
const similarCommit = array.find(c => c.subject === commit.subject);
|
||||
if (similarCommit) {
|
||||
if (commit.abbreviated_commit !== undefined) {
|
||||
similarCommit.abbreviated_commit += `, ${commit.abbreviated_commit}`;
|
||||
}
|
||||
} else {
|
||||
array.push(commit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ICommitExtended} commit
|
||||
*/
|
||||
push(commit) {
|
||||
if (!commit.scope) {
|
||||
CommitGroup._pushOrMerge(this.commits, commit);
|
||||
return;
|
||||
}
|
||||
|
||||
const scope = this.scopes.get(commit.scope) || {commits: []};
|
||||
CommitGroup._pushOrMerge(scope.commits, commit);
|
||||
this.scopes.set(commit.scope, scope);
|
||||
}
|
||||
|
||||
get isEmpty() {
|
||||
return this.commits.length === 0 && this.scopes.size === 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Groups all commits by type and scopes
|
||||
* @param {ICommit[]} commits
|
||||
* @returns {Map<string, CommitGroup>}
|
||||
*/
|
||||
function getGroupedCommits(commits) {
|
||||
const parsedCommits = commits.map(setCommitTypeAndScope);
|
||||
|
||||
const types = new Map(
|
||||
supportedTypes.map(id => ([id, new CommitGroup()])),
|
||||
);
|
||||
|
||||
for (const parsedCommit of parsedCommits) {
|
||||
const typeId = parsedCommit.type;
|
||||
const type = types.get(typeId);
|
||||
type.push(parsedCommit);
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return markdown list with commits
|
||||
* @param {ICommitExtended[]} commits
|
||||
* @param {string} pad
|
||||
* @returns {string}
|
||||
*/
|
||||
function getCommitsList(commits, pad = '') {
|
||||
let changelog = '';
|
||||
for (const commit of commits) {
|
||||
changelog += `${pad}- ${commit.subject}.`;
|
||||
|
||||
if (commit.abbreviated_commit !== undefined) {
|
||||
changelog += ` (${commit.abbreviated_commit})`;
|
||||
}
|
||||
|
||||
changelog += '\r\n';
|
||||
|
||||
if (commit.body === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const body = commit.body.replace('[skip ci]', '').trim();
|
||||
if (body !== '') {
|
||||
changelog += `${
|
||||
body
|
||||
.split(/\r*\n+/)
|
||||
.filter(s => !!s.trim())
|
||||
.map(s => `${pad} ${s}`)
|
||||
.join('\r\n')
|
||||
}${'\r\n'}`;
|
||||
}
|
||||
}
|
||||
|
||||
return changelog;
|
||||
}
|
||||
|
||||
|
||||
function replaceHeader(str) {
|
||||
switch (str) {
|
||||
case 'feat':
|
||||
return 'New Features';
|
||||
case 'fix':
|
||||
return 'Bug Fixes';
|
||||
case 'docs':
|
||||
return 'Documentation Changes';
|
||||
case 'build':
|
||||
return 'Build System';
|
||||
case 'chore':
|
||||
return 'Chores';
|
||||
case 'ci':
|
||||
return 'Continuous Integration';
|
||||
case 'refactor':
|
||||
return 'Refactors';
|
||||
case 'style':
|
||||
return 'Code Style Changes';
|
||||
case 'test':
|
||||
return 'Tests';
|
||||
case 'perf':
|
||||
return 'Performance improvements';
|
||||
case 'revert':
|
||||
return 'Reverts';
|
||||
case 'deps':
|
||||
return 'Dependency updates';
|
||||
case 'other':
|
||||
return 'Other Changes';
|
||||
default:
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return markdown string with changelog
|
||||
* @param {Map<string, CommitGroup>} groups
|
||||
*/
|
||||
function getChangeLog(groups) {
|
||||
|
||||
let changelog = '';
|
||||
|
||||
for (const [typeId, group] of groups) {
|
||||
if (group.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changelog += `### ${replaceHeader(typeId)}${'\r\n'}`;
|
||||
|
||||
for (const [scopeId, scope] of group.scopes) {
|
||||
if (scope.commits.length) {
|
||||
changelog += `- #### ${replaceHeader(scopeId)}${'\r\n'}`;
|
||||
changelog += getCommitsList(scope.commits, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
if (group.commits.length) {
|
||||
changelog += getCommitsList(group.commits);
|
||||
}
|
||||
|
||||
changelog += ('\r\n' + '\r\n');
|
||||
}
|
||||
|
||||
return changelog.trim();
|
||||
}
|
||||
|
||||
|
||||
function escapeData(s) {
|
||||
return String(s)
|
||||
.replace(/%/g, '%25')
|
||||
.replace(/\r/g, '%0D')
|
||||
.replace(/\n/g, '%0A');
|
||||
}
|
||||
|
||||
try {
|
||||
const commits = getCommits();
|
||||
const grouped = getGroupedCommits(commits);
|
||||
const changelog = getChangeLog(grouped);
|
||||
process.stdout.write('::set-output name=release-note::' + escapeData(changelog) + '\r\n');
|
||||
// require('fs').writeFileSync('../CHANGELOG.md', changelog, {encoding: 'utf-8'})
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
58
webapp/.github/renovate.json
vendored
Normal file
58
webapp/.github/renovate.json
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base",
|
||||
":semanticCommits",
|
||||
":automergeTypes",
|
||||
":disableDependencyDashboard"
|
||||
],
|
||||
"labels": [
|
||||
"dependencies"
|
||||
],
|
||||
"baseBranches": [
|
||||
"main"
|
||||
],
|
||||
"bumpVersion": "patch",
|
||||
"patch": {
|
||||
"automerge": true
|
||||
},
|
||||
"minor": {
|
||||
"automerge": true
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"packageNames": [
|
||||
"node",
|
||||
"npm"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"depTypeList": [
|
||||
"devDependencies"
|
||||
],
|
||||
"semanticCommitType": "build"
|
||||
},
|
||||
{
|
||||
"matchSourceUrlPrefixes": [
|
||||
"https://github.com/vitejs/vite/"
|
||||
],
|
||||
"groupName": "Vite monorepo packages",
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^@typescript-eslint",
|
||||
"^eslint"
|
||||
],
|
||||
"automerge": true,
|
||||
"groupName": "eslint"
|
||||
},
|
||||
{
|
||||
"matchPackageNames": [
|
||||
"electron"
|
||||
],
|
||||
"separateMajorMinor": false
|
||||
}
|
||||
],
|
||||
"rangeStrategy": "pin"
|
||||
}
|
40
webapp/.github/workflows/lint.yml
vendored
Normal file
40
webapp/.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: Linters
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '**.js'
|
||||
- '**.ts'
|
||||
- '**.vue'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/lint.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.js'
|
||||
- '**.ts'
|
||||
- '**.vue'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/lint.yml'
|
||||
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
# TODO: Install not all dependencies, but only those required for this workflow
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- run: npm run lint
|
145
webapp/.github/workflows/release.yml
vendored
Normal file
145
webapp/.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.spec.js'
|
||||
- '.idea'
|
||||
- '.gitignore'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/release.yml'
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
draft:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release-note: ${{ steps.release-note.outputs.release-note }}
|
||||
version: ${{ steps.version.outputs.build-version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Get last git tag
|
||||
id: tag
|
||||
run: echo "::set-output name=last-tag::$(git describe --tags --abbrev=0 || git rev-list --max-parents=0 ${{github.ref}})"
|
||||
|
||||
- name: Generate release notes
|
||||
uses: ./.github/actions/release-notes
|
||||
id: release-note
|
||||
with:
|
||||
from: ${{ steps.tag.outputs.last-tag }}
|
||||
to: ${{ github.ref }}
|
||||
include-commit-body: true
|
||||
include-abbreviated-commit: true
|
||||
|
||||
- name: Get version from current date
|
||||
id: version
|
||||
run: echo "::set-output name=build-version::$(node -e "try{console.log(require('./electron-builder.config.js').extraMetadata.version)}catch(e){console.error(e);process.exit(1)}")"
|
||||
|
||||
|
||||
- name: Waiting on All checks
|
||||
uses: lewagon/wait-on-check-action@v0.2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
running-workflow-name: 'draft'
|
||||
|
||||
- name: Delete outdated drafts
|
||||
uses: hugo19941994/delete-draft-releases@v1.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create Release Draft
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.github_token }}
|
||||
with:
|
||||
prerelease: true
|
||||
draft: true
|
||||
tag_name: v${{ steps.version.outputs.build-version }}
|
||||
name: v${{ steps.version.outputs.build-version }}
|
||||
body: ${{ steps.release-note.outputs.release-note }}
|
||||
|
||||
upload_artifacts:
|
||||
needs: [ draft ]
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest ]
|
||||
# To compile the application for different platforms, use:
|
||||
# os: [ macos-latest, ubuntu-latest, windows-latest ]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
# The easiest way to transfer release notes to a compiled application is create `release-notes.md` in the build resources.
|
||||
# See https://github.com/electron-userland/electron-builder/issues/1511#issuecomment-310160119
|
||||
- name: Prepare release notes
|
||||
env:
|
||||
RELEASE_NOTE: ${{ needs.draft.outputs.release-note }}
|
||||
run: echo "$RELEASE_NOTE" >> ./buildResources/release-notes.md
|
||||
|
||||
# Compile app and upload artifacts
|
||||
- name: Compile & release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
env:
|
||||
VITE_APP_VERSION: ${{ needs.draft.outputs.version }}
|
||||
with:
|
||||
build_script_name: build
|
||||
args: --config electron-builder.config.js
|
||||
|
||||
# GitHub token, automatically provided to the action
|
||||
# (No need to define this secret in the repo settings)
|
||||
github_token: ${{ secrets.github_token }}
|
||||
|
||||
# If the commit is tagged with a version (e.g. "v1.0.0"),
|
||||
# release the app after building
|
||||
release: true
|
||||
|
||||
# Sometimes the build may fail due to a connection problem with Apple, GitHub, etc. servers.
|
||||
# This option will restart the build as many attempts as possible
|
||||
max_attempts: 3
|
||||
|
||||
|
||||
# Code Signing params
|
||||
|
||||
# Base64-encoded code signing certificate for Windows
|
||||
# windows_certs: ''
|
||||
|
||||
# Password for decrypting `windows_certs`
|
||||
# windows_certs_password: ''
|
||||
|
||||
# Base64-encoded code signing certificate for macOS
|
||||
# mac_certs: ''
|
||||
|
||||
# Password for decrypting `mac_certs`
|
||||
# mac_certs_password: ''
|
41
webapp/.github/workflows/tests.yml
vendored
Normal file
41
webapp/.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'tests/**'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/tests.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'tests/**'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/tests.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
jobs:
|
||||
e2e:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest ]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
# TODO: Install not all dependencies, but only those required for this workflow
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- run: npm test
|
42
webapp/.github/workflows/typechecking.yml
vendored
Normal file
42
webapp/.github/workflows/typechecking.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
name: Typechecking
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '**.ts'
|
||||
- '**.vue'
|
||||
- '**/tsconfig.json'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/typechecking.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.ts'
|
||||
- '**.vue'
|
||||
- '**/tsconfig.json'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/typechecking.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
jobs:
|
||||
typescript:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
# TODO: Install not all dependencies, but only those required for this workflow
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
# Type checking is divided into three separate commands for more convenient logs
|
||||
- run: npm run typecheck-main
|
||||
- run: npm run typecheck-preload
|
||||
- run: npm run typecheck-renderer
|
44
webapp/.github/workflows/update-electron-vendors.yml
vendored
Normal file
44
webapp/.github/workflows/update-electron-vendors.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
name: Update Electon vendors versions
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'package-lock.json'
|
||||
|
||||
|
||||
concurrency:
|
||||
group: update-electron-vendors-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
|
||||
jobs:
|
||||
node-chrome:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
# TODO: Install not all dependencies, but only those required for this workflow
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- run: node ./scripts/update-electron-vendors.js
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
delete-branch: true
|
||||
commit-message: Update electron vendors
|
||||
branch: autoupdates/electron-vendors
|
||||
title: Update electron vendors
|
||||
body: Updated versions of electron vendors in `electron-vendors.config.json` and `.browserslistrc` files
|
56
webapp/.gitignore
vendored
Normal file
56
webapp/.gitignore
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
*.local
|
||||
thumbs.db
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
.idea/artifacts
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/modules.xml
|
||||
.idea/*.iml
|
||||
.idea/modules
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
/.idea/csv-plugin.xml
|
21
webapp/LICENSE
Normal file
21
webapp/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Alex Kozack
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
200
webapp/README.md
Normal file
200
webapp/README.md
Normal file
@ -0,0 +1,200 @@
|
||||
# Vite Electron Builder Boilerplate v2
|
||||
|
||||
[![GitHub issues by-label](https://img.shields.io/github/issues/cawa-93/vite-electron-builder/help%20wanted?label=issues%20need%20help&logo=github)](https://github.com/cawa-93/vite-electron-builder/issues?q=label%3A%22help+wanted%22+is%3Aopen+is%3Aissue)
|
||||
[![Minimal node version](https://img.shields.io/static/v1?label=node&message=%3E=14.16&logo=node.js&color)](https://nodejs.org/about/releases/)
|
||||
[![Minimal npm version](https://img.shields.io/static/v1?label=npm&message=%3E=7.7&logo=npm&color)](https://github.com/npm/cli/releases)
|
||||
|
||||
> Vite+Electron = 🔥
|
||||
|
||||
This is a secure template for electron applications. Written following the latest safety requirements, recommendations and best practices.
|
||||
|
||||
Under the hood is used [Vite] — super fast, nextgen bundler, and [electron-builder] for compilation.
|
||||
|
||||
|
||||
___
|
||||
### Support
|
||||
- This template maintained by [Alex Kozack][cawa-93-github]. You can [💖 sponsor him][cawa-93-sponsor] for continued development of this template.
|
||||
|
||||
- Found a problem? Pull requests are welcome.
|
||||
|
||||
- If you have ideas, questions or suggestions - **Welcome to [discussions](https://github.com/cawa-93/vite-electron-builder/discussions)**. 😊
|
||||
___
|
||||
|
||||
|
||||
|
||||
|
||||
## Get started
|
||||
|
||||
Follow these steps to get started with this template:
|
||||
|
||||
1. Click the **[Use this template](https://github.com/cawa-93/vite-electron-builder/generate)** button (you must be logged in) or just clone this repo.
|
||||
2. If you want use another package manager don't forget edit [`.github/workflows`](/.github/workflows) -- it uses `npm` by default.
|
||||
|
||||
That's all you need. 😉
|
||||
|
||||
**Note**: This template uses npm v7 feature — [**Installing Peer Dependencies Automatically**](https://github.com/npm/rfcs/blob/latest/implemented/0025-install-peer-deps.md). If you are using a different package manager, you may need to install some peerDependencies manually.
|
||||
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
### Electron [![Electron version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/dev/electron?label=%20)][electron]
|
||||
- Template use the latest electron version with all the latest security patches.
|
||||
- The architecture of the application is built according to the security [guids](https://www.electronjs.org/docs/tutorial/security) and best practices.
|
||||
- The latest version of the [electron-builder] is used to compile the application.
|
||||
|
||||
|
||||
### Vite [![Vite version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/dev/vite?label=%20)][vite]
|
||||
- [Vite] is used to bundle all source codes. This is an extremely fast packer that has a bunch of great features. You can learn more about how it is arranged in [this](https://youtu.be/xXrhg26VCSc) video.
|
||||
- Vite [supports](https://vitejs.dev/guide/env-and-mode.html) reading `.env` files. You can also specify types of your environment variables in [`types/vite-env.d.ts`](types/vite-env.d.ts).
|
||||
|
||||
Vite provides you with many useful features, such as: `TypeScript`, `TSX/JSX`, `CSS/JSON Importing`, `CSS Modules`, `Web Assembly` and much more.
|
||||
|
||||
[See all Vite features](https://vitejs.dev/guide/features.html).
|
||||
|
||||
|
||||
### TypeScript [![TypeScript version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/dev/typescript?label=%20)][typescript] (optional)
|
||||
- The Latest TypeScript is used for all source code.
|
||||
- **Vite** supports TypeScript out of the box. However, it does not support type checking.
|
||||
- Code formatting rules follow the latest TypeScript recommendations and best practices thanks to [@typescript-eslint/eslint-plugin](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin).
|
||||
|
||||
**[See this discussion](https://github.com/cawa-93/vite-electron-builder/discussions/339)** if you want completly remove TypeScript.
|
||||
|
||||
|
||||
### Vue [![Vue version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/vue?label=%20)][vue] (optional)
|
||||
- By default, web pages are built using [Vue]. However, you can easily change it. Or do not use additional frameworks at all. (See [React fork](https://github.com/soulsam480/vite-electron-react-starter))
|
||||
- Also, by default, the [vue-router] version [![Vue-router version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/vue-router?label=%20)][vue-router] is used.
|
||||
- Code formatting rules follow the latest Vue recommendations and best practices thanks to [eslint-plugin-vue].
|
||||
- Installed [Vue.js devtools beta](https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg) with Vue 3 support.
|
||||
|
||||
See [examples of web pages for different frameworks](https://github.com/vitejs/vite/tree/main/packages/create-vite).
|
||||
|
||||
### Continuous Integration
|
||||
- The configured workflow for check the types for each push and PR.
|
||||
- The configured workflow for check the code style for each push and PR.
|
||||
- **Automatic tests** used [playwright]. Simple, automated test check:
|
||||
- Does the main window created and visible?
|
||||
- Is the main window not empty?
|
||||
- Is dev tools closed?
|
||||
- Is preload script loaded?
|
||||
|
||||
|
||||
### Continuous delivery
|
||||
- Each time you push changes to the `main` branch, [`release`](.github/workflows/release.yml) workflow starts, which creates release draft.
|
||||
- The version is automatically set based on the current date in the format `yy.mm.dd-minutes`.
|
||||
- Notes are automatically generated and added to the release draft.
|
||||
- Code signing supported. See [`compile` job in `release` workflow](.github/workflows/release.yml).
|
||||
- **Auto-update is supported**. After the release will be published, all client applications will download the new version and install updates silently.
|
||||
|
||||
|
||||
## Status
|
||||
|
||||
This template was created to make my work easier. It may not be universal, but I try to keep it that way.
|
||||
|
||||
I am actively involved in its development. But I do not guarantee that this template will be maintained in the future.
|
||||
|
||||
|
||||
**At the moment, there are the following problems:**
|
||||
|
||||
- ⚠ Playwright has **experimental** support for Electron.
|
||||
- ⚠ Release notes are created automatically based on commit history. [`.github/actions/release-notes`](.github/actions/release-notes) is used for generation. It may not provide some scenarios. If you encounter a problem - write about it.
|
||||
- ⏳ I want to migrate all code base to ESM. But because Nodejs ecosystem is unprepared I have not known whether this will give more benefits or more inconvenience.
|
||||
|
||||
Some improvement or problems can be listed in [issues](https://github.com/cawa-93/vite-electron-builder/issues).
|
||||
|
||||
**Pull requests are welcome**.
|
||||
|
||||
## How it works
|
||||
The template required a minimum [dependencies](package.json). Only **Vite** is used for building, nothing more.
|
||||
|
||||
### Project Structure
|
||||
|
||||
The structure of this template is very similar to the structure of a monorepo.
|
||||
|
||||
The entire source code of the program is divided into three modules (packages) that are bundled each independently:
|
||||
- [`packages/main`](packages/main)
|
||||
Electron [**main script**](https://www.electronjs.org/docs/tutorial/quick-start#create-the-main-script-file).
|
||||
- [`packages/preload`](packages/preload)
|
||||
Used in `BrowserWindow.webPreferences.preload`. See [Checklist: Security Recommendations](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content).
|
||||
- [`packages/renderer`](packages/renderer)
|
||||
Electron [**web page**](https://www.electronjs.org/docs/tutorial/quick-start#create-a-web-page).
|
||||
|
||||
### Build web resources
|
||||
|
||||
Packages `main` and `preload` are built in [library mode](https://vitejs.dev/guide/build.html#library-mode) as it is a simple javascript.
|
||||
`renderer` package build as regular web app.
|
||||
|
||||
The build of web resources is performed in the [`scripts/build.js`](scripts/build.js). Its analogue is a sequential call to `vite build` for each package.
|
||||
|
||||
### Compile App
|
||||
Next step is run packaging and compilation a ready for distribution Electron app for macOS, Windows and Linux with "auto update" support out of the box.
|
||||
|
||||
To do this, using the [electron-builder]:
|
||||
- In npm script `compile`: This script is configured to compile the application as quickly as possible. It is not ready for distribution, is compiled only for the current platform and is used for debugging.
|
||||
- In GitHub Action: The application is compiled for any platform and ready-to-distribute files are automatically added to the draft GitHub release.
|
||||
|
||||
|
||||
### Using Node.js API in renderer
|
||||
According to [Electron's security guidelines](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content), Node.js integration is disabled for remote content. This means that **you cannot call any Node.js api in the `packages/renderer` directly**. To do this, you **must** describe the interface in the `packages/preload` where Node.js api is allowed:
|
||||
```ts
|
||||
// packages/preload/src/index.ts
|
||||
import {readFile} from 'fs/promises'
|
||||
|
||||
const api = {
|
||||
readConfig: () => readFile('/path/to/config.json', {encoding: 'utf-8'}),
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('electron', api)
|
||||
```
|
||||
|
||||
```ts
|
||||
// packages/renderer/src/App.vue
|
||||
import {useElectron} from '/@/use/electron'
|
||||
|
||||
const {readConfig} = useElectron()
|
||||
```
|
||||
|
||||
[Read more about Security Considerations](https://www.electronjs.org/docs/tutorial/context-isolation#security-considerations).
|
||||
|
||||
|
||||
### Modes and Environment Variables
|
||||
All environment variables set as part of the `import.meta`, so you can access them as follows: `import.meta.env`.
|
||||
|
||||
You can also specify types of your environment variables in [`types/vite-env.d.ts`](types/vite-env.d.ts).
|
||||
|
||||
The mode option is used to specify the value of `import.meta.env.MODE` and the corresponding environment variables files that needs to be loaded.
|
||||
|
||||
By default, there are two modes:
|
||||
- `production` is used by default
|
||||
- `development` is used by `npm run watch` script
|
||||
|
||||
When running building, environment variables are loaded from the following files in your project root:
|
||||
|
||||
```
|
||||
.env # loaded in all cases
|
||||
.env.local # loaded in all cases, ignored by git
|
||||
.env.[mode] # only loaded in specified env mode
|
||||
.env.[mode].local # only loaded in specified env mode, ignored by git
|
||||
```
|
||||
|
||||
**Note:** only variables prefixed with `VITE_` are exposed to your code (e.g. `VITE_SOME_KEY=123`) and `SOME_KEY=123` will not. You can access `VITE_SOME_KEY` using `import.meta.env.VITE_SOME_KEY`. This is because the `.env` files may be used by some users for server-side or build scripts and may contain sensitive information that should not be exposed in code shipped to browsers.
|
||||
|
||||
|
||||
|
||||
## Contribution
|
||||
|
||||
See [Contributing Guide](contributing.md).
|
||||
|
||||
|
||||
[vite]: https://github.com/vitejs/vite/
|
||||
[electron]: https://github.com/electron/electron
|
||||
[electron-builder]: https://github.com/electron-userland/electron-builder
|
||||
[vue]: https://github.com/vuejs/vue-next
|
||||
[vue-router]: https://github.com/vuejs/vue-router-next/
|
||||
[typescript]: https://github.com/microsoft/TypeScript/
|
||||
[playwright]: https://playwright.dev
|
||||
[vue-tsc]: https://github.com/johnsoncodehk/vue-tsc
|
||||
[eslint-plugin-vue]: https://github.com/vuejs/eslint-plugin-vue
|
||||
[cawa-93-github]: https://github.com/cawa-93/
|
||||
[cawa-93-sponsor]: https://www.patreon.com/Kozack/
|
0
webapp/buildResources/.gitkeep
Normal file
0
webapp/buildResources/.gitkeep
Normal file
BIN
webapp/buildResources/icon.icns
Normal file
BIN
webapp/buildResources/icon.icns
Normal file
Binary file not shown.
BIN
webapp/buildResources/icon.png
Normal file
BIN
webapp/buildResources/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
27
webapp/contributing.md
Normal file
27
webapp/contributing.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Contributing
|
||||
|
||||
First and foremost, thank you! We appreciate that you want to contribute to vite-electron-builder, your time is valuable, and your contributions mean a lot to us.
|
||||
|
||||
## Issues
|
||||
|
||||
Do not create issues about bumping dependencies unless a bug has been identified, and you can demonstrate that it effects this library.
|
||||
|
||||
**Help us to help you**
|
||||
|
||||
Remember that we’re here to help, but not to make guesses about what you need help with:
|
||||
|
||||
- Whatever bug or issue you're experiencing, assume that it will not be as obvious to the maintainers as it is to you.
|
||||
- Spell it out completely. Keep in mind that maintainers need to think about _all potential use cases_ of a library. It's important that you explain how you're using a library so that maintainers can make that connection and solve the issue.
|
||||
|
||||
_It can't be understated how frustrating and draining it can be to maintainers to have to ask clarifying questions on the most basic things, before it's even possible to start debugging. Please try to make the best use of everyone's time involved, including yourself, by providing this information up front._
|
||||
|
||||
|
||||
## Repo Setup
|
||||
The package manager used to install and link dependencies must be npm v7 or later.
|
||||
|
||||
1. Clone repo
|
||||
1. `npm run watch` start electron app in watch mode.
|
||||
1. `npm run compile` build app but for local debugging only.
|
||||
1. `npm run lint` lint your code.
|
||||
1. `npm run typecheck` Run typescript check.
|
||||
1. `npm run test` Run app test.
|
23
webapp/electron-builder.config.js
Normal file
23
webapp/electron-builder.config.js
Normal file
@ -0,0 +1,23 @@
|
||||
if (process.env.VITE_APP_VERSION === undefined) {
|
||||
const now = new Date;
|
||||
process.env.VITE_APP_VERSION = `${now.getUTCFullYear() - 2000}.${now.getUTCMonth() + 1}.${now.getUTCDate()}-${now.getUTCHours() * 60 + now.getUTCMinutes()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {import('electron-builder').Configuration}
|
||||
* @see https://www.electron.build/configuration/configuration
|
||||
*/
|
||||
const config = {
|
||||
directories: {
|
||||
output: 'dist',
|
||||
buildResources: 'buildResources',
|
||||
},
|
||||
files: [
|
||||
'packages/**/dist/**',
|
||||
],
|
||||
extraMetadata: {
|
||||
version: process.env.VITE_APP_VERSION,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
4
webapp/electron-vendors.config.json
Normal file
4
webapp/electron-vendors.config.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"chrome": "94",
|
||||
"node": "16"
|
||||
}
|
12797
webapp/package-lock.json
generated
Normal file
12797
webapp/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
54
webapp/package.json
Normal file
54
webapp/package.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "vite-electron-builder",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=v14.14",
|
||||
"npm": ">=7.7"
|
||||
},
|
||||
"main": "packages/main/dist/index.cjs",
|
||||
"scripts": {
|
||||
"build": "node scripts/build.js",
|
||||
"precompile": "cross-env MODE=production npm run build",
|
||||
"compile": "electron-builder build --config electron-builder.config.js --dir --config.asar=false",
|
||||
"pretest": "npm run build",
|
||||
"test": "node tests/app.spec.js",
|
||||
"watch": "node scripts/watch.js",
|
||||
"lint": "eslint . --ext js,ts,vue",
|
||||
"typecheck-main": "tsc --noEmit -p packages/main/tsconfig.json",
|
||||
"typecheck-preload": "tsc --noEmit -p packages/preload/tsconfig.json",
|
||||
"typecheck-renderer": "vue-tsc --noEmit -p packages/renderer/tsconfig.json",
|
||||
"typecheck": "npm run typecheck-main && npm run typecheck-preload && npm run typecheck-renderer"
|
||||
},
|
||||
"browserslist": [
|
||||
"Chrome 94"
|
||||
],
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "npx lint-staged",
|
||||
"pre-push": "npm run typecheck"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,vue}": "eslint --cache --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/electron-devtools-installer": "2.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.32.0",
|
||||
"@vitejs/plugin-vue": "1.9.2",
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "15.1.0",
|
||||
"electron-builder": "22.11.7",
|
||||
"electron-devtools-installer": "3.2.0",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-plugin-vue": "7.18.0",
|
||||
"lint-staged": "11.1.2",
|
||||
"playwright": "1.15.1",
|
||||
"simple-git-hooks": "2.6.1",
|
||||
"typescript": "4.4.2",
|
||||
"vite": "2.6.2",
|
||||
"vue-tsc": "0.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-updater": "4.6.0",
|
||||
"vue": "3.2.19",
|
||||
"vue-router": "4.0.11"
|
||||
}
|
||||
}
|
94
webapp/packages/main/src/index.ts
Normal file
94
webapp/packages/main/src/index.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import {app, BrowserWindow} from 'electron';
|
||||
import {join} from 'path';
|
||||
import {URL} from 'url';
|
||||
|
||||
|
||||
const isSingleInstance = app.requestSingleInstanceLock();
|
||||
|
||||
if (!isSingleInstance) {
|
||||
app.quit();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
app.disableHardwareAcceleration();
|
||||
|
||||
// Install "Vue.js devtools"
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
app.whenReady()
|
||||
.then(() => import('electron-devtools-installer'))
|
||||
.then(({default: installExtension, VUEJS3_DEVTOOLS}) => installExtension(VUEJS3_DEVTOOLS, {
|
||||
loadExtensionOptions: {
|
||||
allowFileAccess: true,
|
||||
},
|
||||
}))
|
||||
.catch(e => console.error('Failed install extension:', e));
|
||||
}
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
const createWindow = async () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
show: false, // Use 'ready-to-show' event to show window
|
||||
webPreferences: {
|
||||
nativeWindowOpen: true,
|
||||
preload: join(__dirname, '../../preload/dist/index.cjs'),
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* If you install `show: true` then it can cause issues when trying to close the window.
|
||||
* Use `show: false` and listener events `ready-to-show` to fix these issues.
|
||||
*
|
||||
* @see https://github.com/electron/electron/issues/25012
|
||||
*/
|
||||
mainWindow.on('ready-to-show', () => {
|
||||
mainWindow?.show();
|
||||
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
mainWindow?.webContents.openDevTools();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* URL for main window.
|
||||
* Vite dev server for development.
|
||||
* `file://../renderer/index.html` for production and test
|
||||
*/
|
||||
const pageUrl = import.meta.env.MODE === 'development' && import.meta.env.VITE_DEV_SERVER_URL !== undefined
|
||||
? import.meta.env.VITE_DEV_SERVER_URL
|
||||
: new URL('../renderer/dist/index.html', 'file://' + __dirname).toString();
|
||||
|
||||
|
||||
await mainWindow.loadURL(pageUrl);
|
||||
};
|
||||
|
||||
|
||||
app.on('second-instance', () => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.whenReady()
|
||||
.then(createWindow)
|
||||
.catch((e) => console.error('Failed create window:', e));
|
||||
|
||||
|
||||
// Auto-updates
|
||||
if (import.meta.env.PROD) {
|
||||
app.whenReady()
|
||||
.then(() => import('electron-updater'))
|
||||
.then(({autoUpdater}) => autoUpdater.checkForUpdatesAndNotify())
|
||||
.catch((e) => console.error('Failed check updates:', e));
|
||||
}
|
||||
|
17
webapp/packages/main/tsconfig.json
Normal file
17
webapp/packages/main/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"/@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
},
|
||||
"files": [
|
||||
"src/index.ts"
|
||||
],
|
||||
"include": [
|
||||
"../../types/**/*.d.ts"
|
||||
]
|
||||
}
|
53
webapp/packages/main/vite.config.js
Normal file
53
webapp/packages/main/vite.config.js
Normal file
@ -0,0 +1,53 @@
|
||||
import {node} from '../../electron-vendors.config.json';
|
||||
import {join} from 'path';
|
||||
import {builtinModules} from 'module';
|
||||
|
||||
const PACKAGE_ROOT = __dirname;
|
||||
|
||||
|
||||
/**
|
||||
* @type {import('vite').UserConfig}
|
||||
* @see https://vitejs.dev/config/
|
||||
*/
|
||||
const config = {
|
||||
mode: process.env.MODE,
|
||||
root: PACKAGE_ROOT,
|
||||
envDir: process.cwd(),
|
||||
resolve: {
|
||||
alias: {
|
||||
'/@/': join(PACKAGE_ROOT, 'src') + '/',
|
||||
},
|
||||
},
|
||||
build: {
|
||||
sourcemap: 'inline',
|
||||
target: `node${node}`,
|
||||
outDir: 'dist',
|
||||
assetsDir: '.',
|
||||
minify: process.env.MODE === 'development' ? false : 'terser',
|
||||
terserOptions: {
|
||||
ecma: 2020,
|
||||
compress: {
|
||||
passes: 2,
|
||||
},
|
||||
safari10: false,
|
||||
},
|
||||
lib: {
|
||||
entry: 'src/index.ts',
|
||||
formats: ['cjs'],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
'electron',
|
||||
'electron-devtools-installer',
|
||||
...builtinModules,
|
||||
],
|
||||
output: {
|
||||
entryFileNames: '[name].cjs',
|
||||
},
|
||||
},
|
||||
emptyOutDir: true,
|
||||
brotliSize: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
17
webapp/packages/preload/src/index.ts
Normal file
17
webapp/packages/preload/src/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {contextBridge} from 'electron';
|
||||
|
||||
const apiKey = 'electron';
|
||||
/**
|
||||
* @see https://github.com/electron/electron/issues/21437#issuecomment-573522360
|
||||
*/
|
||||
const api: ElectronApi = {
|
||||
versions: process.versions,
|
||||
};
|
||||
|
||||
/**
|
||||
* The "Main World" is the JavaScript context that your main renderer code runs in.
|
||||
* By default, the page you load in your renderer executes code in this world.
|
||||
*
|
||||
* @see https://www.electronjs.org/docs/api/context-bridge
|
||||
*/
|
||||
contextBridge.exposeInMainWorld(apiKey, api);
|
18
webapp/packages/preload/tsconfig.json
Normal file
18
webapp/packages/preload/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"/@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"types/electron-api.d.ts",
|
||||
"src/index.ts"
|
||||
],
|
||||
"include": [
|
||||
"../../types/**/*.d.ts"
|
||||
]
|
||||
}
|
9
webapp/packages/preload/types/electron-api.d.ts
vendored
Normal file
9
webapp/packages/preload/types/electron-api.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
interface ElectronApi {
|
||||
readonly versions: Readonly<NodeJS.ProcessVersions>
|
||||
}
|
||||
|
||||
declare interface Window {
|
||||
electron: Readonly<ElectronApi>
|
||||
electronRequire?: NodeRequire
|
||||
}
|
51
webapp/packages/preload/vite.config.js
Normal file
51
webapp/packages/preload/vite.config.js
Normal file
@ -0,0 +1,51 @@
|
||||
import {chrome} from '../../electron-vendors.config.json';
|
||||
import {join} from 'path';
|
||||
import {builtinModules} from 'module';
|
||||
|
||||
const PACKAGE_ROOT = __dirname;
|
||||
|
||||
/**
|
||||
* @type {import('vite').UserConfig}
|
||||
* @see https://vitejs.dev/config/
|
||||
*/
|
||||
const config = {
|
||||
mode: process.env.MODE,
|
||||
root: PACKAGE_ROOT,
|
||||
envDir: process.cwd(),
|
||||
resolve: {
|
||||
alias: {
|
||||
'/@/': join(PACKAGE_ROOT, 'src') + '/',
|
||||
},
|
||||
},
|
||||
build: {
|
||||
sourcemap: 'inline',
|
||||
target: `chrome${chrome}`,
|
||||
outDir: 'dist',
|
||||
assetsDir: '.',
|
||||
minify: process.env.MODE === 'development' ? false : 'terser',
|
||||
terserOptions: {
|
||||
ecma: 2020,
|
||||
compress: {
|
||||
passes: 2,
|
||||
},
|
||||
safari10: false,
|
||||
},
|
||||
lib: {
|
||||
entry: 'src/index.ts',
|
||||
formats: ['cjs'],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
'electron',
|
||||
...builtinModules,
|
||||
],
|
||||
output: {
|
||||
entryFileNames: '[name].cjs',
|
||||
},
|
||||
},
|
||||
emptyOutDir: true,
|
||||
brotliSize: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
15
webapp/packages/renderer/.eslintrc.json
Normal file
15
webapp/packages/renderer/.eslintrc.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": false
|
||||
},
|
||||
"extends": [
|
||||
/** @see https://eslint.vuejs.org/rules/ */
|
||||
"plugin:vue/vue3-recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
}
|
||||
}
|
15
webapp/packages/renderer/assets/logo.svg
Normal file
15
webapp/packages/renderer/assets/logo.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#41D1FF"/>
|
||||
<stop offset="1" stop-color="#BD34FE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFEA83"/>
|
||||
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||
<stop offset="1" stop-color="#FFA800"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
13
webapp/packages/renderer/index.html
Normal file
13
webapp/packages/renderer/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' blob:">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./src/index.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
31
webapp/packages/renderer/src/App.vue
Normal file
31
webapp/packages/renderer/src/App.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<img
|
||||
alt="Vue logo"
|
||||
src="../assets/logo.svg"
|
||||
width="300"
|
||||
>
|
||||
<app-navigation />
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue';
|
||||
import AppNavigation from '/@/components/AppNavigation.vue';
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
components: {
|
||||
AppNavigation,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
</style>
|
36
webapp/packages/renderer/src/components/About.vue
Normal file
36
webapp/packages/renderer/src/components/About.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<h2 id="versions">
|
||||
Lib versions
|
||||
</h2>
|
||||
<div>
|
||||
<ul aria-labelledby="versions">
|
||||
<li
|
||||
v-for="(version, lib) in versions"
|
||||
:key="lib"
|
||||
>
|
||||
<strong>{{ lib }}</strong>: v{{ version }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue';
|
||||
import {useElectron} from '/@/use/electron';
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
setup() {
|
||||
const {versions} = useElectron();
|
||||
// It makes no sense to make "versions" reactive
|
||||
return {versions};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
text-align: left;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
27
webapp/packages/renderer/src/components/AppNavigation.vue
Normal file
27
webapp/packages/renderer/src/components/AppNavigation.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<nav>
|
||||
<router-link to="/">
|
||||
Home
|
||||
</router-link>
|
||||
<span> | </span>
|
||||
<router-link to="/about">
|
||||
About
|
||||
</router-link>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppNavigation',
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
nav {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
51
webapp/packages/renderer/src/components/Home.vue
Normal file
51
webapp/packages/renderer/src/components/Home.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a
|
||||
href="https://github.com/cawa-93/vite-electron-builder"
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
>vite-electron-builder documentation</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a
|
||||
href="https://vitejs.dev/guide/features.html"
|
||||
target="_blank"
|
||||
>Vite Documentation</a> |
|
||||
<a
|
||||
href="https://v3.vuejs.org/"
|
||||
target="_blank"
|
||||
>Vue 3 Documentation</a>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<button @click="count++">
|
||||
count is: {{ count }}
|
||||
</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>renderer/components/Home.vue</code> to test hot module replacement.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, ref} from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HelloWorld',
|
||||
setup() {
|
||||
const count = ref(0);
|
||||
|
||||
return {count};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
7
webapp/packages/renderer/src/index.ts
Normal file
7
webapp/packages/renderer/src/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import {createApp} from 'vue';
|
||||
import App from '/@/App.vue';
|
||||
import router from '/@/router';
|
||||
|
||||
createApp(App)
|
||||
.use(router)
|
||||
.mount('#app');
|
12
webapp/packages/renderer/src/router.ts
Normal file
12
webapp/packages/renderer/src/router.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import {createRouter, createWebHashHistory} from 'vue-router';
|
||||
import Home from '/@/components/Home.vue';
|
||||
|
||||
const routes = [
|
||||
{path: '/', name: 'Home', component: Home},
|
||||
{path: '/about', name: 'About', component: () => import('/@/components/About.vue')}, // Lazy load route component
|
||||
];
|
||||
|
||||
export default createRouter({
|
||||
routes,
|
||||
history: createWebHashHistory(),
|
||||
});
|
3
webapp/packages/renderer/src/use/electron.ts
Normal file
3
webapp/packages/renderer/src/use/electron.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function useElectron(): Readonly<ElectronApi> {
|
||||
return window.electron;
|
||||
}
|
21
webapp/packages/renderer/tsconfig.json
Normal file
21
webapp/packages/renderer/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"/@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
"lib": ["ESNext", "dom", "dom.iterable"]
|
||||
},
|
||||
|
||||
"include": [
|
||||
"src/**/*.vue",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"types/**/*.d.ts",
|
||||
"../../types/**/*.d.ts",
|
||||
"../preload/types/electron-api.d.ts"
|
||||
]
|
||||
}
|
6
webapp/packages/renderer/types/shims-vue.d.ts
vendored
Normal file
6
webapp/packages/renderer/types/shims-vue.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
51
webapp/packages/renderer/vite.config.js
Normal file
51
webapp/packages/renderer/vite.config.js
Normal file
@ -0,0 +1,51 @@
|
||||
/* eslint-env node */
|
||||
|
||||
import {chrome} from '../../electron-vendors.config.json';
|
||||
import {join} from 'path';
|
||||
import {builtinModules} from 'module';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
const PACKAGE_ROOT = __dirname;
|
||||
|
||||
/**
|
||||
* @type {import('vite').UserConfig}
|
||||
* @see https://vitejs.dev/config/
|
||||
*/
|
||||
const config = {
|
||||
mode: process.env.MODE,
|
||||
root: PACKAGE_ROOT,
|
||||
resolve: {
|
||||
alias: {
|
||||
'/@/': join(PACKAGE_ROOT, 'src') + '/',
|
||||
},
|
||||
},
|
||||
plugins: [vue()],
|
||||
base: '',
|
||||
server: {
|
||||
fs: {
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
build: {
|
||||
sourcemap: true,
|
||||
target: `chrome${chrome}`,
|
||||
outDir: 'dist',
|
||||
assetsDir: '.',
|
||||
terserOptions: {
|
||||
ecma: 2020,
|
||||
compress: {
|
||||
passes: 2,
|
||||
},
|
||||
safari10: false,
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
...builtinModules,
|
||||
],
|
||||
},
|
||||
emptyOutDir: true,
|
||||
brotliSize: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
43
webapp/scripts/build.js
Normal file
43
webapp/scripts/build.js
Normal file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/node
|
||||
const {build} = require('vite');
|
||||
const {dirname} = require('path');
|
||||
|
||||
/** @type 'production' | 'development' | 'test' */
|
||||
const mode = process.env.MODE = process.env.MODE || 'production';
|
||||
|
||||
const packagesConfigs = [
|
||||
'packages/main/vite.config.js',
|
||||
'packages/preload/vite.config.js',
|
||||
'packages/renderer/vite.config.js',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Run `vite build` for config file
|
||||
*/
|
||||
const buildByConfig = (configFile) => build({configFile, mode});
|
||||
(async () => {
|
||||
try {
|
||||
const totalTimeLabel = 'Total bundling time';
|
||||
console.time(totalTimeLabel);
|
||||
|
||||
for (const packageConfigPath of packagesConfigs) {
|
||||
|
||||
const consoleGroupName = `${dirname(packageConfigPath)}/`;
|
||||
console.group(consoleGroupName);
|
||||
|
||||
const timeLabel = 'Bundling time';
|
||||
console.time(timeLabel);
|
||||
|
||||
await buildByConfig(packageConfigPath);
|
||||
|
||||
console.timeEnd(timeLabel);
|
||||
console.groupEnd();
|
||||
console.log('\n'); // Just for pretty print
|
||||
}
|
||||
console.timeEnd(totalTimeLabel);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
58
webapp/scripts/update-electron-vendors.js
Normal file
58
webapp/scripts/update-electron-vendors.js
Normal file
@ -0,0 +1,58 @@
|
||||
const {writeFile, readFile} = require('fs/promises');
|
||||
const {execSync} = require('child_process');
|
||||
const electron = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Returns versions of electron vendors
|
||||
* The performance of this feature is very poor and can be improved
|
||||
* @see https://github.com/electron/electron/issues/28006
|
||||
*
|
||||
* @returns {NodeJS.ProcessVersions}
|
||||
*/
|
||||
function getVendors() {
|
||||
const output = execSync(`${electron} -p "JSON.stringify(process.versions)"`, {
|
||||
env: {'ELECTRON_RUN_AS_NODE': '1'},
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
return JSON.parse(output);
|
||||
}
|
||||
|
||||
|
||||
function formattedJSON(obj) {
|
||||
return JSON.stringify(obj, null, 2) + '\n';
|
||||
}
|
||||
|
||||
function updateVendors() {
|
||||
const electronRelease = getVendors();
|
||||
|
||||
const nodeMajorVersion = electronRelease.node.split('.')[0];
|
||||
const chromeMajorVersion = electronRelease.v8.split('.')[0] + electronRelease.v8.split('.')[1];
|
||||
|
||||
const packageJSONPath = path.resolve(process.cwd(), 'package.json');
|
||||
|
||||
return Promise.all([
|
||||
writeFile('./electron-vendors.config.json',
|
||||
formattedJSON({
|
||||
chrome: chromeMajorVersion,
|
||||
node: nodeMajorVersion,
|
||||
}),
|
||||
),
|
||||
|
||||
readFile(packageJSONPath).then(JSON.parse).then((packageJSON) => {
|
||||
if (!packageJSON || !Array.isArray(packageJSON.browserslist)) {
|
||||
throw new Error(`Can't find browserslist in ${packageJSONPath}`);
|
||||
}
|
||||
|
||||
packageJSON.browserslist = [`Chrome ${chromeMajorVersion}`];
|
||||
|
||||
return writeFile(packageJSONPath, formattedJSON(packageJSON));
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
updateVendors().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
113
webapp/scripts/watch.js
Normal file
113
webapp/scripts/watch.js
Normal file
@ -0,0 +1,113 @@
|
||||
#!/usr/bin/node
|
||||
|
||||
const {createServer, build, createLogger} = require('vite');
|
||||
const electronPath = require('electron');
|
||||
const {spawn} = require('child_process');
|
||||
|
||||
|
||||
/** @type 'production' | 'development' | 'test' */
|
||||
const mode = process.env.MODE = process.env.MODE || 'development';
|
||||
|
||||
|
||||
/** @type {import('vite').LogLevel} */
|
||||
const LOG_LEVEL = 'warn';
|
||||
|
||||
|
||||
/** @type {import('vite').InlineConfig} */
|
||||
const sharedConfig = {
|
||||
mode,
|
||||
build: {
|
||||
watch: {},
|
||||
},
|
||||
logLevel: LOG_LEVEL,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param configFile
|
||||
* @param writeBundle
|
||||
* @param name
|
||||
* @returns {Promise<import('vite').RollupOutput | Array<import('vite').RollupOutput> | import('vite').RollupWatcher>}
|
||||
*/
|
||||
const getWatcher = ({name, configFile, writeBundle}) => {
|
||||
return build({
|
||||
...sharedConfig,
|
||||
configFile,
|
||||
plugins: [{name, writeBundle}],
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Start or restart App when source files are changed
|
||||
* @param {import('vite').ViteDevServer} viteDevServer
|
||||
* @returns {Promise<import('vite').RollupOutput | Array<import('vite').RollupOutput> | import('vite').RollupWatcher>}
|
||||
*/
|
||||
const setupMainPackageWatcher = (viteDevServer) => {
|
||||
// Write a value to an environment variable to pass it to the main process.
|
||||
{
|
||||
const protocol = `http${viteDevServer.config.server.https ? 's' : ''}:`;
|
||||
const host = viteDevServer.config.server.host || 'localhost';
|
||||
const port = viteDevServer.config.server.port; // Vite searches for and occupies the first free port: 3000, 3001, 3002 and so on
|
||||
const path = '/';
|
||||
process.env.VITE_DEV_SERVER_URL = `${protocol}//${host}:${port}${path}`;
|
||||
}
|
||||
|
||||
const logger = createLogger(LOG_LEVEL, {
|
||||
prefix: '[main]',
|
||||
});
|
||||
|
||||
/** @type {ChildProcessWithoutNullStreams | null} */
|
||||
let spawnProcess = null;
|
||||
|
||||
return getWatcher({
|
||||
name: 'reload-app-on-main-package-change',
|
||||
configFile: 'packages/main/vite.config.js',
|
||||
writeBundle() {
|
||||
if (spawnProcess !== null) {
|
||||
spawnProcess.kill('SIGINT');
|
||||
spawnProcess = null;
|
||||
}
|
||||
|
||||
spawnProcess = spawn(String(electronPath), ['.']);
|
||||
|
||||
spawnProcess.stdout.on('data', d => d.toString().trim() && logger.warn(d.toString(), {timestamp: true}));
|
||||
spawnProcess.stderr.on('data', d => d.toString().trim() && logger.error(d.toString(), {timestamp: true}));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Start or restart App when source files are changed
|
||||
* @param {import('vite').ViteDevServer} viteDevServer
|
||||
* @returns {Promise<import('vite').RollupOutput | Array<import('vite').RollupOutput> | import('vite').RollupWatcher>}
|
||||
*/
|
||||
const setupPreloadPackageWatcher = (viteDevServer) => {
|
||||
return getWatcher({
|
||||
name: 'reload-page-on-preload-package-change',
|
||||
configFile: 'packages/preload/vite.config.js',
|
||||
writeBundle() {
|
||||
viteDevServer.ws.send({
|
||||
type: 'full-reload',
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const viteDevServer = await createServer({
|
||||
...sharedConfig,
|
||||
configFile: 'packages/renderer/vite.config.js',
|
||||
});
|
||||
|
||||
await viteDevServer.listen();
|
||||
|
||||
await setupPreloadPackageWatcher(viteDevServer);
|
||||
await setupMainPackageWatcher(viteDevServer);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
66
webapp/tests/app.spec.js
Normal file
66
webapp/tests/app.spec.js
Normal file
@ -0,0 +1,66 @@
|
||||
const {_electron: electron} = require('playwright');
|
||||
const {strict: assert} = require('assert');
|
||||
|
||||
// Playwright has EXPERIMENTAL electron support.
|
||||
(async () => {
|
||||
const electronApp = await electron.launch({args: ['.']});
|
||||
|
||||
/**
|
||||
* App main window state
|
||||
* @type {{isVisible: boolean; isDevToolsOpened: boolean; isCrashed: boolean}}
|
||||
*/
|
||||
const windowState = await electronApp.evaluate(({BrowserWindow}) => {
|
||||
const mainWindow = BrowserWindow.getAllWindows()[0];
|
||||
|
||||
const getState = () => ({
|
||||
isVisible: mainWindow.isVisible(),
|
||||
isDevToolsOpened: mainWindow.webContents.isDevToolsOpened(),
|
||||
isCrashed: mainWindow.webContents.isCrashed(),
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
if (mainWindow.isVisible()) {
|
||||
resolve(getState());
|
||||
} else
|
||||
mainWindow.once('ready-to-show', () => setTimeout(() => resolve(getState()), 0));
|
||||
});
|
||||
});
|
||||
|
||||
// Check main window state
|
||||
assert.ok(windowState.isVisible, 'Main window not visible');
|
||||
assert.ok(!windowState.isDevToolsOpened, 'DevTools opened');
|
||||
assert.ok(!windowState.isCrashed, 'Window crashed');
|
||||
|
||||
/**
|
||||
* Rendered Main window web-page
|
||||
* @type {Page}
|
||||
*/
|
||||
const page = await electronApp.firstWindow();
|
||||
|
||||
|
||||
// Check web-page content
|
||||
const element = await page.$('#app', {strict: true});
|
||||
assert.notStrictEqual(element, null, 'Can\'t find root element');
|
||||
assert.notStrictEqual((await element.innerHTML()).trim(), '', 'Window content is empty');
|
||||
|
||||
|
||||
// Checking the framework.
|
||||
// It is assumed that on the main screen there is a `<button>` that changes its contents after clicking.
|
||||
const button = await page.$('button');
|
||||
const originalBtnText = await button.textContent();
|
||||
|
||||
await button.click();
|
||||
const newBtnText = await button.textContent();
|
||||
|
||||
assert.ok(originalBtnText !== newBtnText, 'The button did not change the contents after clicking');
|
||||
|
||||
// Check Preload script
|
||||
const renderedExposedApi = await page.evaluate(() => globalThis.electron);
|
||||
const realVersions = await electronApp.evaluate(() => process.versions);
|
||||
|
||||
assert.notStrictEqual(renderedExposedApi, undefined, 'In renderer `globalThis.electron` is undefined');
|
||||
assert.strictEqual(renderedExposedApi?.versions?.electron, realVersions.electron);
|
||||
|
||||
// Close app
|
||||
await electronApp.close();
|
||||
})();
|
18
webapp/tsconfig.json
Normal file
18
webapp/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"sourceMap": true,
|
||||
"moduleResolution": "Node",
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"isolatedModules": true,
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
],
|
||||
"lib": ["ESNext"]
|
||||
},
|
||||
}
|
0
webapp/types/.gitkeep
Normal file
0
webapp/types/.gitkeep
Normal file
15
webapp/types/vite-env.d.ts
vendored
Normal file
15
webapp/types/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
/**
|
||||
* Describes all existing environment variables and their types.
|
||||
* Assists in autocomplete and typechecking
|
||||
*
|
||||
* @see https://github.com/vitejs/vite/blob/eef51cb37db98a1ad9a541bdd3cd74736ff8488d/packages/vite/types/importMeta.d.ts#L62-L69 Base Interface
|
||||
*/
|
||||
interface ImportMetaEnv {
|
||||
|
||||
/**
|
||||
* The value of the variable is set in scripts/watch.js and depend on packages/main/vite.config.js
|
||||
*/
|
||||
VITE_DEV_SERVER_URL: undefined | string;
|
||||
}
|
25
webapp/vetur.config.js
Normal file
25
webapp/vetur.config.js
Normal file
@ -0,0 +1,25 @@
|
||||
/** @type {import('vls').VeturConfig} */
|
||||
module.exports = {
|
||||
settings: {
|
||||
'vetur.useWorkspaceDependencies': true,
|
||||
'vetur.experimental.templateInterpolationService': true,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
root: './packages/renderer',
|
||||
tsconfig: './tsconfig.json',
|
||||
snippetFolder: './.vscode/vetur/snippets',
|
||||
globalComponents: [
|
||||
'./src/components/**/*.vue',
|
||||
],
|
||||
},
|
||||
{
|
||||
root: './packages/main',
|
||||
tsconfig: './tsconfig.json',
|
||||
},
|
||||
{
|
||||
root: './packages/preload',
|
||||
tsconfig: './tsconfig.json',
|
||||
},
|
||||
],
|
||||
};
|
3898
webapp/yarn.lock
Normal file
3898
webapp/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user