├── .gitignore
├── LICENSE
├── README.md
├── bin
└── create-vite-electron.js
├── package.json
├── project-script
├── base.ts
├── build.ts
├── dev.ts
├── index.ts
└── tsconfig.json
├── scripts
└── build.js
├── src
├── index.ts
└── lib
│ ├── project.ts
│ └── utils.ts
├── template-js
├── index.html
├── scripts
│ └── project.js
└── src
│ └── main
│ ├── entry.js
│ └── protocol.js
├── template-ts
├── index.html
├── scripts
│ └── project.js
└── src
│ └── main
│ ├── entry.ts
│ └── protocol.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # CVEP
6 |
7 | ### Create the Electron project based on the next generation development server vite and vue3.
8 |
9 |
10 | > Project use vite2.0,vue3, and the latest electron.Additional items to be added such as ElementPlus, Vuex, etc.
11 | >
12 | > **Compatibility Note:** Vite requires [Node.js](https://nodejs.org/en/) version >=12.0.0.
13 |
14 |
15 | - ⏱ Quickly create a project
16 | - ⚡️ Lightning Fast HMR
17 | - 🛠️ Dev&&Build
18 | - 📦 No configuration
19 |
20 |
21 | ## >Use
22 |
23 | With NPM:
24 |
25 | ```
26 | $ npm install cvep -g
27 | ```
28 |
29 | With Yarn:
30 |
31 | ```
32 | $ yarn global add cvep
33 | ```
34 |
35 | Then follow the prompts!
36 |
37 | ```
38 | cvep //Follow the flow
39 | ```
40 |
41 | or
42 |
43 | ```
44 | cvep -
45 | ```
46 |
47 | | arg | description |
48 | | ------------------ | ----------------------------------- |
49 | | -J \| --javascript | Based on Javascript create Project |
50 | | -T \| --typescript | Based on typescript create Project |
51 |
52 | At this point the project is created!
53 |
54 | ## >Dev And Build
55 |
56 | Use ViteConfig to build and develop the VUE !!!
57 |
58 | Use the PackageJson Build attribute to package the project !!!
59 |
60 | With NPM:
61 |
62 | ```
63 | $ npm run dev
64 | $ npm run build
65 | ```
66 |
67 | With Yarn:
68 |
69 | ```
70 | $ yarn dev
71 | $ yarn build
72 | ```
73 |
74 |
--------------------------------------------------------------------------------
/bin/create-vite-electron.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | require('../dist/index')
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cvep",
3 | "version": "1.0.2",
4 | "author": "wwog",
5 | "license": "MIT",
6 | "keywords": [
7 | "vue3",
8 | "vite",
9 | "electron",
10 | "projectCreate",
11 | "fast",
12 | "dev",
13 | "build"
14 | ],
15 | "description": "Create the Electron project based on the next generation development server vite and vue3.",
16 | "homepage": "https://github.com/wwog/cvep",
17 | "repository": "wwog/cvep",
18 | "bugs": {
19 | "url": "https://github.com/wwog/cvep/issues"
20 | },
21 | "bin": {
22 | "cvep": "bin/create-vite-electron.js"
23 | },
24 | "files": [
25 | "bin",
26 | "dist",
27 | "template-js",
28 | "template-ts"
29 | ],
30 | "engines": {
31 | "node": ">=12.0.0"
32 | },
33 | "scripts": {
34 | "build": "node scripts/build.js && tsc"
35 | },
36 | "dependencies": {
37 | "esbuild": "^0.8.46",
38 | "@vitejs/create-app": "^2.0.0",
39 | "enquirer": "^2.3.6",
40 | "minimist": "^1.2.5"
41 | },
42 | "devDependencies": {
43 | "@types/minimist": "^1.2.1",
44 | "@types/node": "^14.14.25",
45 | "typescript": "^4.1.5"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/project-script/base.ts:
--------------------------------------------------------------------------------
1 | import { resolveConfig } from 'vite'
2 | import * as fs from 'fs'
3 | import { join } from 'path'
4 | import { buildSync } from 'esbuild'
5 | import * as chalk from 'chalk'
6 |
7 | export class Base {
8 | protected readonly root = join(__dirname, '../')
9 | protected readonly pkg = join(this.root, 'package.json')
10 | protected readonly main = join(this.root, 'src/main')
11 | protected readonly render = join(this.root, 'src/render')
12 | protected outDir = join(this.root, 'dist')
13 | protected mainEntrySrc = '';
14 | protected mainEntry = '';
15 | protected readonly outFlag = {
16 | vite: chalk.green(`[vite] `),
17 | electron: chalk.blue(`[electron] `),
18 | control: chalk.rgb(190, 33, 64)(`[control] `)
19 | }
20 | protected async init() {
21 | let pkg
22 | try {
23 | pkg = require(this.pkg)
24 | }catch(error){
25 | console.error(error)
26 | process.exit();
27 | }
28 | //main入口,首先根据pkgJson为主,如果没有则在main目录寻找entry文件
29 | if (pkg.main) {
30 | this.mainEntrySrc = join(this.root, pkg.main)
31 | } else {
32 | let isFind = false
33 | for (const file of fs.readdirSync(this.main)) {
34 | if (file.startsWith('entry')) {
35 | isFind = true;
36 | this.mainEntrySrc = join(this.main, file)
37 | break
38 | }
39 | }
40 | if(!isFind){
41 | console.error(new Error(`not find electron mainProcess Entry file!!!`))
42 | }
43 | }
44 | //outDir,vite默认为dist
45 | const viteConfig = await resolveConfig({}, 'build')
46 | this.outDir = viteConfig.build.outDir
47 | //mainOut
48 | this.mainEntry = join(this.outDir, '__entry.js')
49 | }
50 | protected buildMain(isDev = true) {
51 | if (isDev) process.env.CVE_ENV = 'development'
52 | return buildSync({
53 | bundle: true,
54 | entryPoints: [this.mainEntrySrc],
55 | external: ['electron'],
56 | platform: 'node',
57 | minify: isDev ? false : true,
58 | outfile: this.mainEntry
59 | })
60 | }
61 | }
62 | export function isObject(arg: any) {
63 | return arg !== null && Object.prototype.toString.call(arg) === '[object Object]'
64 | }
65 |
66 | export function isArray(arg: any) {
67 | return Array.isArray(arg)
68 | }
69 |
70 | export function mergeObject(target: any, ...arg: any[]) {
71 | return arg.reduce((acc, cur) => {
72 | return Object.keys(cur).reduce((subAcc, key) => {
73 | const srcVal = cur[key]
74 | if (isObject(srcVal)) {
75 | subAcc[key] = mergeObject(subAcc[key] ? subAcc[key] : {}, srcVal)
76 | } else if (isArray(srcVal)) {
77 | subAcc[key] = srcVal.map((item: any, idx: number) => {
78 | if (isObject(item)) {
79 | const curAccVal = subAcc[key] ? subAcc[key] : []
80 | return mergeObject(curAccVal[idx] ? curAccVal[idx] : {}, item)
81 | } else {
82 | return item
83 | }
84 | })
85 | } else {
86 | subAcc[key] = srcVal
87 | }
88 | return subAcc
89 | }, acc)
90 | }, target)
91 | }
--------------------------------------------------------------------------------
/project-script/build.ts:
--------------------------------------------------------------------------------
1 | import { Base } from "./base";
2 | import { build as viteBuild } from "vite";
3 | import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4 | import { build as electronBuild } from "electron-builder";
5 | import { join } from "path";
6 | import * as chalk from 'chalk'
7 |
8 | export class Build extends Base {
9 | private async buildRender() {
10 | await viteBuild()
11 | }
12 | private getMIRROR() {
13 | //npmrc ELECTRON_MIRROR
14 | let ELECTRON_MIRROR
15 | let npmrc = join(this.root, '.npmrc')
16 | if (existsSync(npmrc)) {
17 | ELECTRON_MIRROR = readFileSync(npmrc).toString()
18 | let _t = ELECTRON_MIRROR.split(/[(\r\n)\r\n]+/)
19 | for (let i = 0; i < _t.length; i++) {
20 | let key = _t[i].split('=')
21 | if (key[0] == 'ELECTRON_MIRROR') {
22 | ELECTRON_MIRROR = key[1]
23 | break;
24 | }
25 | }
26 | }
27 | return ELECTRON_MIRROR
28 | }
29 | private async buildProject() {
30 | mkdirSync(join(this.outDir, "node_modules"));
31 | let MIRROR = this.getMIRROR()
32 | await electronBuild({
33 | projectDir: this.outDir,
34 | config: {
35 | directories: {
36 | output: join(this.root, "release"),
37 | },
38 | extends: null,
39 | electronDownload: {
40 | mirror: MIRROR
41 | },
42 | }
43 | })
44 | }
45 | private mkPkgJson() {
46 | let pkg
47 | try {
48 | pkg = require(this.pkg);
49 | } catch (error) {
50 | console.error(error)
51 | process.exit()
52 | }
53 |
54 | if (!pkg.main) {
55 | pkg.main = '__entry.js'
56 | }
57 | let electronVersion = pkg.devDependencies.electron.replace("^", "");
58 | delete pkg.scripts;
59 | delete pkg.dependencies;
60 | delete pkg.devDependencies;
61 | pkg.devDependencies = { electron: electronVersion };
62 | writeFileSync(
63 | join(this.outDir, "package.json"),
64 | JSON.stringify(pkg, null, 2),
65 | );
66 | return pkg;
67 | }
68 | async run() {
69 | await this.init().catch(e => {
70 | console.error(chalk.red`[contorlError] init failed:\n${e}`)
71 | })
72 | await this.buildRender()
73 | this.buildMain()
74 | this.mkPkgJson()
75 | await this.buildProject().catch(e => {
76 | console.error(chalk.red`[electronBuilderError] \n${e}`)
77 | process.exit()
78 | })
79 | console.log(this.outFlag.control + ` The packaged application is complete`)
80 | }
81 | }
--------------------------------------------------------------------------------
/project-script/dev.ts:
--------------------------------------------------------------------------------
1 | import { Base } from './base'
2 | import { resolveConfig, createServer, ViteDevServer } from 'vite'
3 | import * as chalk from 'chalk'
4 | import { existsSync, rmdirSync } from 'fs'
5 | import { spawn, ChildProcessWithoutNullStreams } from "child_process";
6 | import { watch } from "chokidar";
7 | import { Server } from 'http';
8 |
9 | export class Dev extends Base {
10 | private port!: number;
11 | private viteServer!: ViteDevServer;
12 | private electron!: ChildProcessWithoutNullStreams;
13 | private isReStart = false
14 | private oldTime!: number;
15 | private async runRender() {
16 | let config = await resolveConfig({}, 'serve')
17 | this.port = config.server.port ? config.server.port : 3000
18 | this.viteServer = await createServer({
19 | server: { port: this.port }
20 | }) as ViteDevServer
21 | let httpServer = this.viteServer.httpServer as Server
22 | if (httpServer != null) {
23 | return new Promise(resolve => {
24 | const onError = (e: Error & { code?: string }) => {
25 | if (e.code === 'EADDRINUSE') {
26 | if (config.server.strictPort) {
27 | console.error(chalk.red`[viteError] Port ${this.port} is already in use`)
28 | process.exit()
29 | } else {
30 | console.log(this.outFlag.vite + `Port ${this.port} is in use, trying another one...`);
31 | httpServer.listen(++this.port)
32 | }
33 | } else {
34 | console.error(chalk.red`[viteError] ${e}`)
35 | process.exit()
36 | }
37 | }
38 | httpServer.on('error', onError)
39 | httpServer.listen(this.port, () => {
40 | httpServer.removeListener('error', onError)
41 | console.log(this.outFlag.vite + `Dev server running at:${this.port}`)
42 | resolve('')
43 | })
44 | })
45 | } else {
46 | console.error(chalk.red`[viteError] httpServer Create a failure`)
47 | }
48 | }
49 | private async runElectron() {
50 | await this.init().catch(e => {
51 | console.error(chalk.red`[contorlError] init failed:\n${e}`)
52 | })
53 | this.buildMain()
54 | let _electron
55 | try {
56 | _electron = require('electron')
57 | } catch (error) {
58 | console.error(error)
59 | process.exit();
60 | }
61 | this.electron = spawn(_electron.toString(), [this.mainEntry])
62 | this.electron.on('close', () => {
63 | if (this.isReStart) {
64 | this.isReStart = false
65 | this.runElectron()
66 | } else {
67 | this.viteServer.close()
68 | process.exit()
69 | }
70 | })
71 | process.on('exit', () => {
72 | if (existsSync(this.outDir)) {
73 | rmdirSync(this.outDir, { recursive: true })
74 | }
75 | })
76 | this.electron.stdout.on('data', chunk => {
77 | let data = chunk.toString() as string
78 | if (data.trim().length > 0) {
79 | console.log(this.outFlag.electron + `${chunk.toString()}`)
80 | }
81 | })
82 | console.log(this.outFlag.control + `Start time: ${Date.now() - this.oldTime}ms`)
83 | }
84 | private mainHMR() {
85 | watch(this.main).on('change', () => {
86 | console.log(this.outFlag.control + `entry file change.restart at ${new Date()}`)
87 | this.isReStart = true
88 | this.electron.kill()
89 | this.oldTime = Date.now()
90 | })
91 | }
92 | async run() {
93 | this.oldTime = Date.now()
94 | await this.runRender()
95 | process.env.CVE_PORT = this.port + ''
96 | await this.runElectron()
97 | this.mainHMR()
98 | }
99 | }
100 | //test
101 | //new Dev().run()
--------------------------------------------------------------------------------
/project-script/index.ts:
--------------------------------------------------------------------------------
1 | import { Dev } from './dev'
2 | import { Build } from './build'
3 | let method = process.argv.slice(2)[0]
4 | async function init() {
5 | switch (method) {
6 | case 'build':
7 | await new Build().run()
8 | break;
9 | case 'dev':
10 | await new Dev().run()
11 | break;
12 | default:
13 | console.log(`The argument should be dev or build`)
14 | }
15 | }
16 |
17 | init().catch(e => {
18 | console.log(e)
19 | })
--------------------------------------------------------------------------------
/project-script/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9 | "lib": ["ESNext"], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | // "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | // "outDir": "./", /* Redirect output structure to the directory. */
18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true, /* Enable all strict type-checking options. */
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | // "noUnusedLocals": true, /* Report errors on unused locals. */
39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
43 |
44 | /* Module Resolution Options */
45 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
49 | // "typeRoots": [], /* List of folders to include type definitions from. */
50 | // "types": [], /* Type declaration files to be included in compilation. */
51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
55 |
56 | /* Source Map Options */
57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
61 |
62 | /* Experimental Options */
63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
65 |
66 | /* Advanced Options */
67 | "skipLibCheck": true, /* Skip type checking of declaration files. */
68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | const { buildSync } = require('esbuild')
2 | const path = require('path')
3 | const fs = require('fs')
4 |
5 | let root = path.join(__dirname, '../')
6 |
7 | function buildProjectScript() {
8 | let templateJs = path.join(root, 'template-js/scripts/project.js')
9 | let result = buildSync({
10 | platform: 'node',
11 | bundle: true,
12 | entryPoints: [path.join(root, 'project-script/index.ts')],
13 | treeShaking: 'ignore-annotations',
14 | external: [
15 | 'vite', 'chalk', 'fs', 'child_process',
16 | 'chokidar', 'http', 'electron-builder', 'path',
17 | 'esbuild'
18 | ],
19 | outfile: templateJs,
20 | minify: false
21 | })
22 | if (result.warnings.length === 0) {
23 | fs.copyFileSync(templateJs, templateJs.replace('-js', '-ts'))
24 | return true
25 | }
26 | return false
27 | }
28 |
29 | buildProjectScript()
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Project } from "./lib/project";
2 | import { prompt } from "enquirer"
3 | import * as parseArgv from "minimist"
4 | import * as fs from 'fs'
5 |
6 | import { emptyDir } from './lib/utils'
7 | let argv = parseArgv.default(process.argv.slice(2), {
8 | alias: {
9 | j: ['J', 'javascript'],
10 | t: ['T', 'typescript'],
11 | }
12 | })
13 | const TEMPLATES = [
14 | 'ts',
15 | 'js',
16 | ]
17 |
18 | async function init() {
19 | let projectName = argv._[0]
20 | if (!projectName) {
21 | const { name }: any = await prompt({
22 | type: 'input',
23 | name: 'name',
24 | message: `Project name:`,
25 | initial: 'electronApp'
26 | })
27 | projectName = name
28 | }
29 | let project = new Project(projectName)
30 | type _t = 'js' | 'ts' | undefined
31 | let template: _t = argv.j ? 'js' : argv.t ? 'ts' : undefined
32 | if (!template) {
33 | const { t }: any = await prompt({
34 | type: 'select',
35 | name: 't',
36 | message: `Select a template:`,
37 | choices: TEMPLATES
38 | })
39 | template = t
40 | }
41 | await project.create(template, async () => {
42 | const existing = fs.readdirSync(project.root)
43 | if (existing.length) {
44 | const { yes }: any = await prompt({
45 | type: 'confirm',
46 | name: 'yes',
47 | initial: 'Y',
48 | message:
49 | `Target directory ${projectName} is not empty.\n` +
50 | `Remove existing files and continue?`
51 | })
52 | if (yes) {
53 | emptyDir(project.root)
54 | } else {
55 | return
56 | }
57 | }
58 | })
59 | const { addItems }: { addItems: any[] } = await prompt({
60 | type: 'multiselect',
61 | name: 'addItems',
62 | message: 'Select addItems:',
63 | initial: 0,
64 | choices: Project.ADDITEMS
65 | })
66 | project.addItem(addItems)
67 | console.log(`\n\n cd ${projectName}\n yarn\n`)
68 | }
69 |
70 | init().then(value => {
71 |
72 | }).catch(e => { console.log(e) })
--------------------------------------------------------------------------------
/src/lib/project.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 | import * as fs from 'fs';
3 | import { copy, emptyDir } from './utils';
4 | import { join } from "path";
5 | let cwd = process.cwd();
6 | export class Project {
7 | public root: string;
8 | private viteTemplate!: string;
9 | protected packagePath: string;
10 | protected createAppDir = path.join(__dirname, '../../node_modules', '@vitejs/create-app')
11 | protected templateDir = '';
12 | static ADDITEMS = [
13 | 'npmrc',
14 | ]
15 | constructor(public projectName: string) {
16 | this.root = join(cwd, projectName)
17 | this.packagePath = join(this.root, 'package.json')
18 | }
19 | async create(language: 'js' | 'ts' = 'ts', existHandle: any) {
20 | let dirName = ''
21 | if (language == 'js') {
22 | dirName = 'template-vue'
23 | } else {
24 | dirName = 'template-vue-ts'
25 | }
26 | if (!fs.existsSync(this.root)) {
27 | fs.mkdirSync(this.root, { recursive: true })
28 | } else {
29 | await existHandle()
30 | }
31 | //将vite模板输出文件夹
32 | this.viteTemplate = path.join(this.createAppDir, dirName)
33 | let files = fs.readdirSync(this.viteTemplate)
34 | for (const file of files) {
35 | this.write(file)
36 | }
37 | this.cleanUnwantedFiles()
38 | this.engineering()
39 | //将项目模板输出到文件夹
40 | this.templateDir = path.join(__dirname, `../../template-${language}`)
41 | files = fs.readdirSync(this.templateDir)
42 | for (const file of files) {
43 | this.write(file, undefined, this.templateDir)
44 | }
45 | //重写pkg
46 | let pkgData = require(this.packagePath)
47 | pkgData.name = this.projectName.toLowerCase()
48 | pkgData.devDependencies['electron'] = '^11.2.3'
49 | pkgData.devDependencies['chalk'] = '^4.1.0'
50 | pkgData.devDependencies['esbuild'] = '^0.8.34'
51 | pkgData.devDependencies['electron-builder'] = '^22.9.1'
52 | pkgData.devDependencies['chokidar'] = '^3.5.1'
53 | pkgData.scripts['vite_dev']=pkgData.scripts['dev']
54 | pkgData.scripts['vite_build']=pkgData.scripts['build']
55 | pkgData.scripts['dev'] = "node ./scripts/project.js dev"
56 | pkgData.scripts['build'] = "node ./scripts/project.js build"
57 | fs.writeFileSync(this.packagePath, JSON.stringify(pkgData, null, 2))
58 | }
59 | addItem(itemArr: string[]) {
60 | for (const item of itemArr) {
61 | //@ts-ignore
62 | if (this[item]) {
63 | //@ts-ignore
64 | this[item]()
65 | }
66 | }
67 | }
68 | private write(file: string, content: any = undefined, templateDir = this.viteTemplate) {
69 | //@ts-ignore
70 | const targetPath = renameFiles[file]
71 | //@ts-ignore
72 | ? path.join(this.root, renameFiles[file])
73 | : path.join(this.root, file)
74 | if (content) {
75 | fs.writeFileSync(targetPath, content)
76 | } else {
77 | copy(path.join(templateDir, file), targetPath)
78 | }
79 | }
80 | private cleanUnwantedFiles() {
81 | fs.unlinkSync(join(this.root, 'index.html'))
82 | emptyDir(join(this.root, 'public'))
83 | fs.rmdirSync(join(this.root, 'public'))
84 | }
85 | private engineering() {
86 | let oldPath = join(this.root, 'src')
87 | let tempPath = join(this.root, 'render')
88 | let newPath = join(this.root, 'src/render')
89 | fs.renameSync(oldPath, tempPath)
90 | fs.mkdirSync(oldPath)
91 | fs.renameSync(tempPath, newPath)
92 | }
93 | private npmrc() {
94 | let fileData = 'ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/\nELECTRON_BUILDER_BINARIES_MIRROR=http://npm.taobao.org/mirrors/electron-builder-binaries/'
95 | this.write('.npmrc', fileData)
96 | }
97 | private elementPlus() {
98 | let pkgData = require(this.packagePath)
99 | pkgData.dependencies['element-plus'] = 'latest'
100 | fs.writeFileSync(this.packagePath, JSON.stringify(pkgData, null, 2))
101 | }
102 | }
103 | const renameFiles = {
104 | _gitignore: '.gitignore'
105 | }
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import * as fs from 'fs'
3 | export function copy(src:string, dest:string) {
4 | const stat = fs.statSync(src)
5 | if (stat.isDirectory()) {
6 | copyDir(src, dest)
7 | } else {
8 | fs.copyFileSync(src, dest)
9 | }
10 | }
11 |
12 | export function copyDir(srcDir:string, destDir:string) {
13 | fs.mkdirSync(destDir, { recursive: true })
14 | for (const file of fs.readdirSync(srcDir)) {
15 | const srcFile = path.resolve(srcDir, file)
16 | const destFile = path.resolve(destDir, file)
17 | copy(srcFile, destFile)
18 | }
19 | }
20 |
21 | export function emptyDir(dir:string) {
22 | if (!fs.existsSync(dir)) {
23 | return
24 | }
25 | for (const file of fs.readdirSync(dir)) {
26 | const abs = path.resolve(dir, file)
27 | // baseline is Node 12 so can't use rmSync :(
28 | if (fs.lstatSync(abs).isDirectory()) {
29 | emptyDir(abs)
30 | fs.rmdirSync(abs)
31 | } else {
32 | fs.unlinkSync(abs)
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/template-js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | newApp
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/template-js/scripts/project.js:
--------------------------------------------------------------------------------
1 | var __create = Object.create;
2 | var __defProp = Object.defineProperty;
3 | var __getProtoOf = Object.getPrototypeOf;
4 | var __hasOwnProp = Object.prototype.hasOwnProperty;
5 | var __getOwnPropNames = Object.getOwnPropertyNames;
6 | var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7 | var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
8 | var __exportStar = (target, module2, desc) => {
9 | if (module2 && typeof module2 === "object" || typeof module2 === "function") {
10 | for (let key of __getOwnPropNames(module2))
11 | if (!__hasOwnProp.call(target, key) && key !== "default")
12 | __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
13 | }
14 | return target;
15 | };
16 | var __toModule = (module2) => {
17 | if (module2 && module2.__esModule)
18 | return module2;
19 | return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", {value: module2, enumerable: true})), module2);
20 | };
21 |
22 | // project-script/base.ts
23 | var import_vite = __toModule(require("vite"));
24 | var fs = __toModule(require("fs"));
25 | var import_path = __toModule(require("path"));
26 | var import_esbuild = __toModule(require("esbuild"));
27 | var chalk = __toModule(require("chalk"));
28 | var Base = class {
29 | constructor() {
30 | this.root = import_path.join(__dirname, "../");
31 | this.pkg = import_path.join(this.root, "package.json");
32 | this.main = import_path.join(this.root, "src/main");
33 | this.render = import_path.join(this.root, "src/render");
34 | this.outDir = import_path.join(this.root, "dist");
35 | this.mainEntrySrc = "";
36 | this.mainEntry = "";
37 | this.outFlag = {
38 | vite: chalk.green(`[vite] `),
39 | electron: chalk.blue(`[electron] `),
40 | control: chalk.rgb(190, 33, 64)(`[control] `)
41 | };
42 | }
43 | async init() {
44 | let pkg;
45 | try {
46 | pkg = require(this.pkg);
47 | } catch (error) {
48 | console.error(error);
49 | process.exit();
50 | }
51 | if (pkg.main) {
52 | this.mainEntrySrc = import_path.join(this.root, pkg.main);
53 | } else {
54 | let isFind = false;
55 | for (const file of fs.readdirSync(this.main)) {
56 | if (file.startsWith("entry")) {
57 | isFind = true;
58 | this.mainEntrySrc = import_path.join(this.main, file);
59 | break;
60 | }
61 | }
62 | if (!isFind) {
63 | console.error(new Error(`not find electron mainProcess Entry file!!!`));
64 | }
65 | }
66 | const viteConfig = await import_vite.resolveConfig({}, "build");
67 | this.outDir = viteConfig.build.outDir;
68 | this.mainEntry = import_path.join(this.outDir, "__entry.js");
69 | }
70 | buildMain(isDev = true) {
71 | if (isDev)
72 | process.env.CVE_ENV = "development";
73 | return import_esbuild.buildSync({
74 | bundle: true,
75 | entryPoints: [this.mainEntrySrc],
76 | external: ["electron"],
77 | platform: "node",
78 | minify: isDev ? false : true,
79 | outfile: this.mainEntry
80 | });
81 | }
82 | };
83 |
84 | // project-script/dev.ts
85 | var import_vite2 = __toModule(require("vite"));
86 | var chalk2 = __toModule(require("chalk"));
87 | var import_fs = __toModule(require("fs"));
88 | var import_child_process = __toModule(require("child_process"));
89 | var import_chokidar = __toModule(require("chokidar"));
90 | var Dev = class extends Base {
91 | constructor() {
92 | super(...arguments);
93 | this.isReStart = false;
94 | }
95 | async runRender() {
96 | let config = await import_vite2.resolveConfig({}, "serve");
97 | this.port = config.server.port ? config.server.port : 3e3;
98 | this.viteServer = await import_vite2.createServer({
99 | server: {port: this.port}
100 | });
101 | let httpServer = this.viteServer.httpServer;
102 | if (httpServer != null) {
103 | return new Promise((resolve) => {
104 | const onError = (e) => {
105 | if (e.code === "EADDRINUSE") {
106 | if (config.server.strictPort) {
107 | console.error(chalk2.red`[viteError] Port ${this.port} is already in use`);
108 | process.exit();
109 | } else {
110 | console.log(this.outFlag.vite + `Port ${this.port} is in use, trying another one...`);
111 | httpServer.listen(++this.port);
112 | }
113 | } else {
114 | console.error(chalk2.red`[viteError] ${e}`);
115 | process.exit();
116 | }
117 | };
118 | httpServer.on("error", onError);
119 | httpServer.listen(this.port, () => {
120 | httpServer.removeListener("error", onError);
121 | console.log(this.outFlag.vite + `Dev server running at:${this.port}`);
122 | resolve("");
123 | });
124 | });
125 | } else {
126 | console.error(chalk2.red`[viteError] httpServer Create a failure`);
127 | }
128 | }
129 | async runElectron() {
130 | await this.init().catch((e) => {
131 | console.error(chalk2.red`[contorlError] init failed:\n${e}`);
132 | });
133 | this.buildMain();
134 | let _electron;
135 | try {
136 | _electron = require("electron");
137 | } catch (error) {
138 | console.error(error);
139 | process.exit();
140 | }
141 | this.electron = import_child_process.spawn(_electron.toString(), [this.mainEntry]);
142 | this.electron.on("close", () => {
143 | if (this.isReStart) {
144 | this.isReStart = false;
145 | this.runElectron();
146 | } else {
147 | this.viteServer.close();
148 | process.exit();
149 | }
150 | });
151 | process.on("exit", () => {
152 | if (import_fs.existsSync(this.outDir)) {
153 | import_fs.rmdirSync(this.outDir, {recursive: true});
154 | }
155 | });
156 | this.electron.stdout.on("data", (chunk) => {
157 | let data = chunk.toString();
158 | if (data.trim().length > 0) {
159 | console.log(this.outFlag.electron + `${chunk.toString()}`);
160 | }
161 | });
162 | console.log(this.outFlag.control + `Start time: ${Date.now() - this.oldTime}ms`);
163 | }
164 | mainHMR() {
165 | import_chokidar.watch(this.main).on("change", () => {
166 | console.log(this.outFlag.control + `entry file change.restart at ${new Date()}`);
167 | this.isReStart = true;
168 | this.electron.kill();
169 | this.oldTime = Date.now();
170 | });
171 | }
172 | async run() {
173 | this.oldTime = Date.now();
174 | await this.runRender();
175 | process.env.CVE_PORT = this.port + "";
176 | await this.runElectron();
177 | this.mainHMR();
178 | }
179 | };
180 |
181 | // project-script/build.ts
182 | var import_vite3 = __toModule(require("vite"));
183 | var import_fs2 = __toModule(require("fs"));
184 | var import_electron_builder = __toModule(require("electron-builder"));
185 | var import_path2 = __toModule(require("path"));
186 | var chalk3 = __toModule(require("chalk"));
187 | var Build = class extends Base {
188 | async buildRender() {
189 | await import_vite3.build();
190 | }
191 | getMIRROR() {
192 | let ELECTRON_MIRROR;
193 | let npmrc = import_path2.join(this.root, ".npmrc");
194 | if (import_fs2.existsSync(npmrc)) {
195 | ELECTRON_MIRROR = import_fs2.readFileSync(npmrc).toString();
196 | let _t = ELECTRON_MIRROR.split(/[(\r\n)\r\n]+/);
197 | for (let i = 0; i < _t.length; i++) {
198 | let key = _t[i].split("=");
199 | if (key[0] == "ELECTRON_MIRROR") {
200 | ELECTRON_MIRROR = key[1];
201 | break;
202 | }
203 | }
204 | }
205 | return ELECTRON_MIRROR;
206 | }
207 | async buildProject() {
208 | import_fs2.mkdirSync(import_path2.join(this.outDir, "node_modules"));
209 | let MIRROR = this.getMIRROR();
210 | await import_electron_builder.build({
211 | projectDir: this.outDir,
212 | config: {
213 | directories: {
214 | output: import_path2.join(this.root, "release")
215 | },
216 | extends: null,
217 | electronDownload: {
218 | mirror: MIRROR
219 | }
220 | }
221 | });
222 | }
223 | mkPkgJson() {
224 | let pkg;
225 | try {
226 | pkg = require(this.pkg);
227 | } catch (error) {
228 | console.error(error);
229 | process.exit();
230 | }
231 | if (!pkg.main) {
232 | pkg.main = "__entry.js";
233 | }
234 | let electronVersion = pkg.devDependencies.electron.replace("^", "");
235 | delete pkg.scripts;
236 | delete pkg.dependencies;
237 | delete pkg.devDependencies;
238 | pkg.devDependencies = {electron: electronVersion};
239 | import_fs2.writeFileSync(import_path2.join(this.outDir, "package.json"), JSON.stringify(pkg, null, 2));
240 | return pkg;
241 | }
242 | async run() {
243 | await this.init().catch((e) => {
244 | console.error(chalk3.red`[contorlError] init failed:\n${e}`);
245 | });
246 | await this.buildRender();
247 | this.buildMain();
248 | this.mkPkgJson();
249 | await this.buildProject().catch((e) => {
250 | console.error(chalk3.red`[electronBuilderError] \n${e}`);
251 | process.exit();
252 | });
253 | console.log(this.outFlag.control + ` The packaged application is complete`);
254 | }
255 | };
256 |
257 | // project-script/index.ts
258 | var method = process.argv.slice(2)[0];
259 | async function init() {
260 | switch (method) {
261 | case "build":
262 | await new Build().run();
263 | break;
264 | case "dev":
265 | await new Dev().run();
266 | break;
267 | default:
268 | console.log(`The argument should be dev or build`);
269 | }
270 | }
271 | init().catch((e) => {
272 | console.log(e);
273 | });
274 |
--------------------------------------------------------------------------------
/template-js/src/main/entry.js:
--------------------------------------------------------------------------------
1 | const { app, BrowserWindow } = require("electron");
2 | const createProtocol = require("./protocol");
3 | const isDevelopment = process.env.CVE_ENV === 'development'
4 |
5 |
6 | app.on('ready', () => {
7 | let win = new BrowserWindow({
8 | show: false,
9 | webPreferences: {
10 | nodeIntegration: true
11 | }
12 | })
13 | win.on('ready-to-show', () => { win.show() })
14 | if (isDevelopment) {
15 | win.loadURL(`http://localhost:${process.env.CVE_PORT}`)
16 | } else {
17 | createProtocol('app')
18 | win.loadURL('app://./index.html')
19 | }
20 | win.webContents.openDevTools()
21 | })
22 |
--------------------------------------------------------------------------------
/template-js/src/main/protocol.js:
--------------------------------------------------------------------------------
1 | const { protocol } = require("electron");
2 | const { join, extname } = require("path");
3 | const { readFile } = require("fs");
4 | const { URL } = require("url");
5 | protocol.registerSchemesAsPrivileged([
6 | { scheme: "app", privileges: { secure: true, standard: true } },
7 | ]);
8 | module.exports = (scheme) => {
9 | protocol.registerBufferProtocol(scheme, (req, res) => {
10 | let pathName = new URL(req.url).pathname;
11 | pathName = decodeURI(pathName);
12 | readFile(join(__dirname, pathName), (e, data) => {
13 | if (e) {
14 | console.error(`Failed to read ${pathName} on ${scheme} protocol`);
15 | }
16 | const extension = extname(pathName).toLowerCase();
17 | let mimeType = "";
18 | switch (extension) {
19 | case ".js":
20 | mimeType = "text/javascript";
21 | break;
22 | case ".html":
23 | mimeType = "text/html";
24 | break;
25 | case ".css":
26 | mimeType = "text/css";
27 | break;
28 | case ".svg":
29 | case ".svgz":
30 | mimeType = "image/svg+xml";
31 | break;
32 | case ".json":
33 | mimeType = "application/json";
34 | break;
35 | case ".wasm":
36 | mimeType = "application/wasm";
37 | }
38 | res({ mimeType, data });
39 | });
40 | });
41 | };
42 |
--------------------------------------------------------------------------------
/template-ts/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | newApp
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/template-ts/scripts/project.js:
--------------------------------------------------------------------------------
1 | var __create = Object.create;
2 | var __defProp = Object.defineProperty;
3 | var __getProtoOf = Object.getPrototypeOf;
4 | var __hasOwnProp = Object.prototype.hasOwnProperty;
5 | var __getOwnPropNames = Object.getOwnPropertyNames;
6 | var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7 | var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
8 | var __exportStar = (target, module2, desc) => {
9 | if (module2 && typeof module2 === "object" || typeof module2 === "function") {
10 | for (let key of __getOwnPropNames(module2))
11 | if (!__hasOwnProp.call(target, key) && key !== "default")
12 | __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
13 | }
14 | return target;
15 | };
16 | var __toModule = (module2) => {
17 | if (module2 && module2.__esModule)
18 | return module2;
19 | return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", {value: module2, enumerable: true})), module2);
20 | };
21 |
22 | // project-script/base.ts
23 | var import_vite = __toModule(require("vite"));
24 | var fs = __toModule(require("fs"));
25 | var import_path = __toModule(require("path"));
26 | var import_esbuild = __toModule(require("esbuild"));
27 | var chalk = __toModule(require("chalk"));
28 | var Base = class {
29 | constructor() {
30 | this.root = import_path.join(__dirname, "../");
31 | this.pkg = import_path.join(this.root, "package.json");
32 | this.main = import_path.join(this.root, "src/main");
33 | this.render = import_path.join(this.root, "src/render");
34 | this.outDir = import_path.join(this.root, "dist");
35 | this.mainEntrySrc = "";
36 | this.mainEntry = "";
37 | this.outFlag = {
38 | vite: chalk.green(`[vite] `),
39 | electron: chalk.blue(`[electron] `),
40 | control: chalk.rgb(190, 33, 64)(`[control] `)
41 | };
42 | }
43 | async init() {
44 | let pkg;
45 | try {
46 | pkg = require(this.pkg);
47 | } catch (error) {
48 | console.error(error);
49 | process.exit();
50 | }
51 | if (pkg.main) {
52 | this.mainEntrySrc = import_path.join(this.root, pkg.main);
53 | } else {
54 | let isFind = false;
55 | for (const file of fs.readdirSync(this.main)) {
56 | if (file.startsWith("entry")) {
57 | isFind = true;
58 | this.mainEntrySrc = import_path.join(this.main, file);
59 | break;
60 | }
61 | }
62 | if (!isFind) {
63 | console.error(new Error(`not find electron mainProcess Entry file!!!`));
64 | }
65 | }
66 | const viteConfig = await import_vite.resolveConfig({}, "build");
67 | this.outDir = viteConfig.build.outDir;
68 | this.mainEntry = import_path.join(this.outDir, "__entry.js");
69 | }
70 | buildMain(isDev = true) {
71 | if (isDev)
72 | process.env.CVE_ENV = "development";
73 | return import_esbuild.buildSync({
74 | bundle: true,
75 | entryPoints: [this.mainEntrySrc],
76 | external: ["electron"],
77 | platform: "node",
78 | minify: isDev ? false : true,
79 | outfile: this.mainEntry
80 | });
81 | }
82 | };
83 |
84 | // project-script/dev.ts
85 | var import_vite2 = __toModule(require("vite"));
86 | var chalk2 = __toModule(require("chalk"));
87 | var import_fs = __toModule(require("fs"));
88 | var import_child_process = __toModule(require("child_process"));
89 | var import_chokidar = __toModule(require("chokidar"));
90 | var Dev = class extends Base {
91 | constructor() {
92 | super(...arguments);
93 | this.isReStart = false;
94 | }
95 | async runRender() {
96 | let config = await import_vite2.resolveConfig({}, "serve");
97 | this.port = config.server.port ? config.server.port : 3e3;
98 | this.viteServer = await import_vite2.createServer({
99 | server: {port: this.port}
100 | });
101 | let httpServer = this.viteServer.httpServer;
102 | if (httpServer != null) {
103 | return new Promise((resolve) => {
104 | const onError = (e) => {
105 | if (e.code === "EADDRINUSE") {
106 | if (config.server.strictPort) {
107 | console.error(chalk2.red`[viteError] Port ${this.port} is already in use`);
108 | process.exit();
109 | } else {
110 | console.log(this.outFlag.vite + `Port ${this.port} is in use, trying another one...`);
111 | httpServer.listen(++this.port);
112 | }
113 | } else {
114 | console.error(chalk2.red`[viteError] ${e}`);
115 | process.exit();
116 | }
117 | };
118 | httpServer.on("error", onError);
119 | httpServer.listen(this.port, () => {
120 | httpServer.removeListener("error", onError);
121 | console.log(this.outFlag.vite + `Dev server running at:${this.port}`);
122 | resolve("");
123 | });
124 | });
125 | } else {
126 | console.error(chalk2.red`[viteError] httpServer Create a failure`);
127 | }
128 | }
129 | async runElectron() {
130 | await this.init().catch((e) => {
131 | console.error(chalk2.red`[contorlError] init failed:\n${e}`);
132 | });
133 | this.buildMain();
134 | let _electron;
135 | try {
136 | _electron = require("electron");
137 | } catch (error) {
138 | console.error(error);
139 | process.exit();
140 | }
141 | this.electron = import_child_process.spawn(_electron.toString(), [this.mainEntry]);
142 | this.electron.on("close", () => {
143 | if (this.isReStart) {
144 | this.isReStart = false;
145 | this.runElectron();
146 | } else {
147 | this.viteServer.close();
148 | process.exit();
149 | }
150 | });
151 | process.on("exit", () => {
152 | if (import_fs.existsSync(this.outDir)) {
153 | import_fs.rmdirSync(this.outDir, {recursive: true});
154 | }
155 | });
156 | this.electron.stdout.on("data", (chunk) => {
157 | let data = chunk.toString();
158 | if (data.trim().length > 0) {
159 | console.log(this.outFlag.electron + `${chunk.toString()}`);
160 | }
161 | });
162 | console.log(this.outFlag.control + `Start time: ${Date.now() - this.oldTime}ms`);
163 | }
164 | mainHMR() {
165 | import_chokidar.watch(this.main).on("change", () => {
166 | console.log(this.outFlag.control + `entry file change.restart at ${new Date()}`);
167 | this.isReStart = true;
168 | this.electron.kill();
169 | this.oldTime = Date.now();
170 | });
171 | }
172 | async run() {
173 | this.oldTime = Date.now();
174 | await this.runRender();
175 | process.env.CVE_PORT = this.port + "";
176 | await this.runElectron();
177 | this.mainHMR();
178 | }
179 | };
180 |
181 | // project-script/build.ts
182 | var import_vite3 = __toModule(require("vite"));
183 | var import_fs2 = __toModule(require("fs"));
184 | var import_electron_builder = __toModule(require("electron-builder"));
185 | var import_path2 = __toModule(require("path"));
186 | var chalk3 = __toModule(require("chalk"));
187 | var Build = class extends Base {
188 | async buildRender() {
189 | await import_vite3.build();
190 | }
191 | getMIRROR() {
192 | let ELECTRON_MIRROR;
193 | let npmrc = import_path2.join(this.root, ".npmrc");
194 | if (import_fs2.existsSync(npmrc)) {
195 | ELECTRON_MIRROR = import_fs2.readFileSync(npmrc).toString();
196 | let _t = ELECTRON_MIRROR.split(/[(\r\n)\r\n]+/);
197 | for (let i = 0; i < _t.length; i++) {
198 | let key = _t[i].split("=");
199 | if (key[0] == "ELECTRON_MIRROR") {
200 | ELECTRON_MIRROR = key[1];
201 | break;
202 | }
203 | }
204 | }
205 | return ELECTRON_MIRROR;
206 | }
207 | async buildProject() {
208 | import_fs2.mkdirSync(import_path2.join(this.outDir, "node_modules"));
209 | let MIRROR = this.getMIRROR();
210 | await import_electron_builder.build({
211 | projectDir: this.outDir,
212 | config: {
213 | directories: {
214 | output: import_path2.join(this.root, "release")
215 | },
216 | extends: null,
217 | electronDownload: {
218 | mirror: MIRROR
219 | }
220 | }
221 | });
222 | }
223 | mkPkgJson() {
224 | let pkg;
225 | try {
226 | pkg = require(this.pkg);
227 | } catch (error) {
228 | console.error(error);
229 | process.exit();
230 | }
231 | if (!pkg.main) {
232 | pkg.main = "__entry.js";
233 | }
234 | let electronVersion = pkg.devDependencies.electron.replace("^", "");
235 | delete pkg.scripts;
236 | delete pkg.dependencies;
237 | delete pkg.devDependencies;
238 | pkg.devDependencies = {electron: electronVersion};
239 | import_fs2.writeFileSync(import_path2.join(this.outDir, "package.json"), JSON.stringify(pkg, null, 2));
240 | return pkg;
241 | }
242 | async run() {
243 | await this.init().catch((e) => {
244 | console.error(chalk3.red`[contorlError] init failed:\n${e}`);
245 | });
246 | await this.buildRender();
247 | this.buildMain();
248 | this.mkPkgJson();
249 | await this.buildProject().catch((e) => {
250 | console.error(chalk3.red`[electronBuilderError] \n${e}`);
251 | process.exit();
252 | });
253 | console.log(this.outFlag.control + ` The packaged application is complete`);
254 | }
255 | };
256 |
257 | // project-script/index.ts
258 | var method = process.argv.slice(2)[0];
259 | async function init() {
260 | switch (method) {
261 | case "build":
262 | await new Build().run();
263 | break;
264 | case "dev":
265 | await new Dev().run();
266 | break;
267 | default:
268 | console.log(`The argument should be dev or build`);
269 | }
270 | }
271 | init().catch((e) => {
272 | console.log(e);
273 | });
274 |
--------------------------------------------------------------------------------
/template-ts/src/main/entry.ts:
--------------------------------------------------------------------------------
1 | import { app, BrowserWindow } from "electron";
2 | import createProtocol from "./protocol";
3 | const isDevelopment = process.env.CVE_ENV === 'development'
4 |
5 | app.on('ready', () => {
6 | let win = new BrowserWindow({
7 | show: false,
8 | webPreferences: {
9 | nodeIntegration: true
10 | }
11 | })
12 | win.on('ready-to-show', () => { win.show() })
13 | if (isDevelopment) {
14 | win.loadURL(`http://localhost:${process.env.CVE_PORT}`)
15 | } else {
16 | createProtocol('app')
17 | win.loadURL('app://./index.html')
18 | }
19 | win.webContents.openDevTools()
20 | })
21 |
--------------------------------------------------------------------------------
/template-ts/src/main/protocol.ts:
--------------------------------------------------------------------------------
1 | import { protocol } from "electron";
2 | import { join, extname } from "path";
3 | import { readFile } from "fs";
4 | import { URL } from "url";
5 | protocol.registerSchemesAsPrivileged([
6 | { scheme: "app", privileges: { secure: true, standard: true } },
7 | ]);
8 | export default (scheme: string) => {
9 | protocol.registerBufferProtocol(scheme, (req, res) => {
10 | let pathName = new URL(req.url).pathname;
11 | pathName = decodeURI(pathName);
12 | readFile(join(__dirname, pathName), (e, data) => {
13 | if (e) {
14 | console.error(`Failed to read ${pathName} on ${scheme} protocol`);
15 | }
16 | const extension = extname(pathName).toLowerCase();
17 | let mimeType = "";
18 | switch (extension) {
19 | case ".js":
20 | mimeType = "text/javascript";
21 | break;
22 | case ".html":
23 | mimeType = "text/html";
24 | break;
25 | case ".css":
26 | mimeType = "text/css";
27 | break;
28 | case ".svg":
29 | case ".svgz":
30 | mimeType = "image/svg+xml";
31 | break;
32 | case ".json":
33 | mimeType = "application/json";
34 | break;
35 | case ".wasm":
36 | mimeType = "application/wasm";
37 | }
38 | res({ mimeType, data });
39 | });
40 | });
41 | };
42 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["./src"],
3 | "compilerOptions": {
4 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
5 |
6 | /* Basic Options */
7 | // "incremental": true, /* Enable incremental compilation */
8 | "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
9 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
10 | // "lib": [], /* Specify library files to be included in the compilation. */
11 | // "allowJs": true, /* Allow javascript files to be compiled. */
12 | // "checkJs": true, /* Report errors in .js files. */
13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
14 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
15 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
16 | // "sourceMap": true, /* Generates corresponding '.map' file. */
17 | // "outFile": "./", /* Concatenate and emit output to single file. */
18 | "outDir": "./dist", /* Redirect output structure to the directory. */
19 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
20 | // "composite": true, /* Enable project compilation */
21 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
22 | // "removeComments": true, /* Do not emit comments to output. */
23 | // "noEmit": true, /* Do not emit outputs. */
24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
27 |
28 | /* Strict Type-Checking Options */
29 | "strict": true, /* Enable all strict type-checking options. */
30 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
31 | // "strictNullChecks": true, /* Enable strict null checks. */
32 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
33 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
34 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
35 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
36 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
37 |
38 | /* Additional Checks */
39 | // "noUnusedLocals": true, /* Report errors on unused locals. */
40 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
41 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
42 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
43 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
44 |
45 | /* Module Resolution Options */
46 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
47 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
48 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
50 | // "typeRoots": [], /* List of folders to include type definitions from. */
51 | // "types": [], /* Type declaration files to be included in compilation. */
52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
53 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
56 |
57 | /* Source Map Options */
58 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
60 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
61 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
62 |
63 | /* Experimental Options */
64 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
66 |
67 | /* Advanced Options */
68 | "skipLibCheck": true, /* Skip type checking of declaration files. */
69 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@types/minimist@^1.2.1":
6 | version "1.2.1"
7 | resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256"
8 | integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==
9 |
10 | "@types/node@^14.14.25":
11 | version "14.14.28"
12 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.28.tgz#cade4b64f8438f588951a6b35843ce536853f25b"
13 | integrity sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g==
14 |
15 | "@vitejs/create-app@^2.0.0":
16 | version "2.0.0"
17 | resolved "https://registry.yarnpkg.com/@vitejs/create-app/-/create-app-2.0.0.tgz#3935079d4b7d04c39115d6019faae8e6f5df66d7"
18 | integrity sha512-ULTNyMjRROo414g53UqGR2L8jFCYbo1PqOeCGUy0MGpiD/y2bct68+yd9Y4KnhIcr2OFF7p5LSEHgKpjiQCWlg==
19 | dependencies:
20 | enquirer "^2.3.6"
21 | kolorist "^1.2.9"
22 | minimist "^1.2.5"
23 |
24 | ansi-colors@^4.1.1:
25 | version "4.1.1"
26 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
27 | integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
28 |
29 | enquirer@^2.3.6:
30 | version "2.3.6"
31 | resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
32 | integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
33 | dependencies:
34 | ansi-colors "^4.1.1"
35 |
36 | esbuild@^0.8.46:
37 | version "0.8.46"
38 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.46.tgz#8fc7230ce3019b12e2553399f0c03875a729c26b"
39 | integrity sha512-xck9sXNCNmjDHCCfxTCyhKTiFuEBweh+IDAhMLOJI990v1Fzii6MyIkT1LbkvjgoVgPX2SK1kpi5eZVGNrl8yg==
40 |
41 | kolorist@^1.2.9:
42 | version "1.2.10"
43 | resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.2.10.tgz#0f97a3f3a1dd75e980765c7cec8a1de258e580c6"
44 | integrity sha512-S3QtGjCHyINclP4LSClgHw4gi/NxTFcSorqD9SWfrREHKtMyGfi6pyDCTbpQaqyZrMAjB4Exde8eco6kejkqQg==
45 |
46 | minimist@^1.2.5:
47 | version "1.2.5"
48 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
49 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
50 |
51 | typescript@^4.1.5:
52 | version "4.1.5"
53 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72"
54 | integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==
55 |
--------------------------------------------------------------------------------