@@ -22,3 +22,6 @@ indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.ts]
|
||||
quote_type= "single"
|
||||
|
||||
1
bin/builders/BuilderFactory.ts
vendored
1
bin/builders/BuilderFactory.ts
vendored
@@ -1,6 +1,7 @@
|
||||
import { IS_MAC } from '@/utils/platform.js';
|
||||
import { IBuilder } from './base.js';
|
||||
import MacBuilder from './MacBuilder.js';
|
||||
import WinBuilder from './WinBulider.js';
|
||||
|
||||
export default class BuilderFactory {
|
||||
static create(): IBuilder {
|
||||
|
||||
38
bin/builders/MacBuilder.ts
vendored
38
bin/builders/MacBuilder.ts
vendored
@@ -7,8 +7,10 @@ import { IBuilder } from './base.js';
|
||||
import { shellExec } from '@/utils/shell.js';
|
||||
// @ts-expect-error 加上resolveJsonModule rollup会打包报错
|
||||
import tauriConf from '../../src-tauri/tauri.conf.json';
|
||||
import { fileURLToPath } from 'url';
|
||||
import log from 'loglevel';
|
||||
import { mergeTauriConfig } from './common.js';
|
||||
import { npmDirectory } from '@/utils/dir.js';
|
||||
import logger from '@/options/logger.js';
|
||||
|
||||
export default class MacBuilder implements IBuilder {
|
||||
async prepare() {
|
||||
@@ -33,34 +35,26 @@ export default class MacBuilder implements IBuilder {
|
||||
|
||||
async build(url: string, options: PakeAppOptions) {
|
||||
log.debug('PakeAppOptions', options);
|
||||
const { name } = options;
|
||||
|
||||
const { width, height, fullscreen, transparent, resizable, identifier, name } = options;
|
||||
|
||||
const tauriConfWindowOptions = {
|
||||
width,
|
||||
height,
|
||||
fullscreen,
|
||||
transparent,
|
||||
resizable,
|
||||
};
|
||||
|
||||
// TODO 下面这块逻辑还可以再拆 目前比较简单
|
||||
Object.assign(tauriConf.tauri.windows[0], { url, ...tauriConfWindowOptions });
|
||||
tauriConf.package.productName = name;
|
||||
tauriConf.tauri.bundle.identifier = identifier;
|
||||
tauriConf.tauri.bundle.icon = [options.icon];
|
||||
|
||||
const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json');
|
||||
await fs.writeFile(configJsonPath, Buffer.from(JSON.stringify(tauriConf), 'utf-8'));
|
||||
await mergeTauriConfig(url, options, tauriConf);
|
||||
|
||||
const code = await shellExec(`cd ${npmDirectory} && npm run build`);
|
||||
const dmgName = `${name}_${tauriConf.package.version}_universal.dmg`;
|
||||
const appPath = this.getBuildedAppPath(npmDirectory, dmgName);
|
||||
await fs.copyFile(appPath, path.resolve(`${name}_universal.dmg`));
|
||||
const distPath = path.resolve(`${name}_universal.dmg`);
|
||||
await fs.copyFile(appPath, distPath);
|
||||
await fs.unlink(appPath);
|
||||
|
||||
logger.success('Build success!');
|
||||
logger.success('You can find the app installer in', distPath);
|
||||
}
|
||||
|
||||
getBuildedAppPath(npmDirectory: string, dmgName: string) {
|
||||
return path.join(npmDirectory, 'src-tauri/target/universal-apple-darwin/release/bundle/dmg', dmgName);
|
||||
return path.join(
|
||||
npmDirectory,
|
||||
'src-tauri/target/universal-apple-darwin/release/bundle/dmg',
|
||||
dmgName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
60
bin/builders/WinBulider.ts
vendored
60
bin/builders/WinBulider.ts
vendored
@@ -0,0 +1,60 @@
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import prompts from 'prompts';
|
||||
import { checkRustInstalled, installRust } from '@/helpers/rust.js';
|
||||
import { PakeAppOptions } from '@/types.js';
|
||||
import { IBuilder } from './base.js';
|
||||
import { shellExec } from '@/utils/shell.js';
|
||||
// @ts-expect-error 加上resolveJsonModule rollup会打包报错
|
||||
import tauriConf from '../../src-tauri/tauri.conf.json';
|
||||
import { fileURLToPath } from 'url';
|
||||
import logger from '@/options/logger.js';
|
||||
import { mergeTauriConfig } from './common.js';
|
||||
import { npmDirectory } from '@/utils/dir.js';
|
||||
|
||||
export default class WinBuilder implements IBuilder {
|
||||
async prepare() {
|
||||
logger.info(
|
||||
'To build the Windows app, you need to install Rust and VS Build Tools.'
|
||||
);
|
||||
logger.info(
|
||||
'See more in https://tauri.app/v1/guides/getting-started/prerequisites#installing\n'
|
||||
);
|
||||
if (checkRustInstalled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await prompts({
|
||||
type: 'confirm',
|
||||
message: 'We detected that you have not installed Rust. Install it now?',
|
||||
name: 'value',
|
||||
});
|
||||
|
||||
if (res.value) {
|
||||
// TODO 国内有可能会超时
|
||||
await installRust();
|
||||
} else {
|
||||
logger.error('Error: Pake needs Rust to package your webapp!!!');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
async build(url: string, options: PakeAppOptions) {
|
||||
logger.debug('PakeAppOptions', options);
|
||||
|
||||
await mergeTauriConfig(url, options, tauriConf);
|
||||
|
||||
const code = await shellExec(`cd ${npmDirectory} && npm run build:windows`);
|
||||
// const dmgName = `${name}_${tauriConf.package.version}_universal.dmg`;
|
||||
// const appPath = this.getBuildedAppPath(npmDirectory, dmgName);
|
||||
// await fs.copyFile(appPath, path.resolve(`${name}_universal.dmg`));
|
||||
}
|
||||
|
||||
getBuildedAppPath(npmDirectory: string, dmgName: string) {
|
||||
return path.join(
|
||||
npmDirectory,
|
||||
'src-tauri/target/universal-apple-darwin/release/bundle/dmg',
|
||||
dmgName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
40
bin/builders/common.ts
vendored
40
bin/builders/common.ts
vendored
@@ -1,4 +1,8 @@
|
||||
import { PakeAppOptions } from '@/types.js';
|
||||
import prompts from 'prompts';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { npmDirectory } from '@/utils/dir.js';
|
||||
|
||||
export async function promptText(message: string, initial?: string) {
|
||||
const response = await prompts({
|
||||
@@ -9,3 +13,39 @@ export async function promptText(message: string, initial?: string) {
|
||||
});
|
||||
return response.content;
|
||||
}
|
||||
|
||||
export async function mergeTauriConfig(
|
||||
url: string,
|
||||
options: PakeAppOptions,
|
||||
tauriConf: any
|
||||
) {
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
fullscreen,
|
||||
transparent,
|
||||
resizable,
|
||||
identifier,
|
||||
name,
|
||||
} = options;
|
||||
|
||||
const tauriConfWindowOptions = {
|
||||
width,
|
||||
height,
|
||||
fullscreen,
|
||||
transparent,
|
||||
resizable,
|
||||
};
|
||||
|
||||
Object.assign(tauriConf.tauri.windows[0], { url, ...tauriConfWindowOptions });
|
||||
tauriConf.package.productName = name;
|
||||
tauriConf.tauri.bundle.identifier = identifier;
|
||||
tauriConf.tauri.bundle.icon = [options.icon];
|
||||
|
||||
|
||||
const configJsonPath = path.join(npmDirectory, 'src-tauri/tauri.conf.json');
|
||||
await fs.writeFile(
|
||||
configJsonPath,
|
||||
Buffer.from(JSON.stringify(tauriConf), 'utf-8')
|
||||
);
|
||||
}
|
||||
|
||||
20
bin/cli.ts
vendored
20
bin/cli.ts
vendored
@@ -1,16 +1,21 @@
|
||||
import { program } from 'commander';
|
||||
import log from 'loglevel';
|
||||
import chalk from 'chalk';
|
||||
import { DEFAULT_PAKE_OPTIONS } from './defaults.js';
|
||||
import { PakeCliOptions } from './types.js';
|
||||
import { validateNumberInput, validateUrlInput } from './utils/validate.js';
|
||||
import handleInputOptions from './options/index.js';
|
||||
import BuilderFactory from './builders/BuilderFactory.js';
|
||||
import log from 'loglevel';
|
||||
import { checkUpdateTips } from './helpers/updater.js';
|
||||
// @ts-expect-error
|
||||
import packageJson from '../../package.json';
|
||||
import logger from './options/logger.js';
|
||||
|
||||
program.version('0.0.1').description('A cli application can package a web page to desktop application');
|
||||
program.version(packageJson.version).description('A cli application can package a web page to desktop application.');
|
||||
|
||||
program
|
||||
.showHelpAfterError()
|
||||
.argument('<url>', 'the web url you want to package', validateUrlInput)
|
||||
.argument('[url]', 'the web url you want to package', validateUrlInput)
|
||||
.option('--name <string>', 'application name')
|
||||
.option('--icon <string>', 'application icon', DEFAULT_PAKE_OPTIONS.icon)
|
||||
.option('--height <number>', 'window height', validateNumberInput, DEFAULT_PAKE_OPTIONS.height)
|
||||
@@ -20,7 +25,14 @@ program
|
||||
.option('--transparent', 'transparent title bar', DEFAULT_PAKE_OPTIONS.transparent)
|
||||
.option('--debug', 'debug', DEFAULT_PAKE_OPTIONS.transparent)
|
||||
.action(async (url: string, options: PakeCliOptions) => {
|
||||
log.setDefaultLevel('info')
|
||||
checkUpdateTips();
|
||||
|
||||
if (!url) {
|
||||
// 直接 pake 不需要出现url提示
|
||||
program.help();
|
||||
}
|
||||
|
||||
log.setDefaultLevel('info');
|
||||
if (options.debug) {
|
||||
log.setLevel('debug');
|
||||
}
|
||||
|
||||
8
bin/helpers/rust.ts
vendored
8
bin/helpers/rust.ts
vendored
@@ -1,12 +1,16 @@
|
||||
import { IS_WIN } from '@/utils/platform.js';
|
||||
import ora from 'ora';
|
||||
import shelljs from 'shelljs';
|
||||
import { shellExec } from '../utils/shell.js';
|
||||
|
||||
const InstallRustScript = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
|
||||
const RustInstallScriptFocMac =
|
||||
"curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
|
||||
const RustInstallScriptForWin = 'winget install --id Rustlang.Rustup';
|
||||
|
||||
export async function installRust() {
|
||||
const spinner = ora('Downloading Rust').start();
|
||||
try {
|
||||
await shellExec(InstallRustScript);
|
||||
await shellExec(IS_WIN ? RustInstallScriptForWin : RustInstallScriptFocMac);
|
||||
spinner.succeed();
|
||||
} catch (error) {
|
||||
console.error('install rust return code', error.message);
|
||||
|
||||
7
bin/helpers/updater.ts
vendored
Normal file
7
bin/helpers/updater.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import updateNotifier from 'update-notifier';
|
||||
// @ts-expect-error
|
||||
import packageJson from '../../package.json';
|
||||
|
||||
export async function checkUpdateTips() {
|
||||
updateNotifier({ pkg: packageJson }).notify();
|
||||
}
|
||||
4
bin/options/icon.ts
vendored
4
bin/options/icon.ts
vendored
@@ -5,7 +5,7 @@ import { dir } from 'tmp-promise';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
import log from 'loglevel';
|
||||
import logger from './logger.js';
|
||||
|
||||
export async function handleIcon(options: PakeAppOptions, url: string) {
|
||||
if (options.icon) {
|
||||
@@ -21,7 +21,7 @@ export async function handleIcon(options: PakeAppOptions, url: string) {
|
||||
}
|
||||
|
||||
export async function inferIcon(name: string, url: string) {
|
||||
log.info('You have not provided an app icon, use the default icon(can use --icon option to assign an icon)')
|
||||
logger.info('You have not provided an app icon, use the default icon.(use --icon option to assign an icon)')
|
||||
const npmDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
return path.join(npmDirectory, 'pake-default.icns');
|
||||
}
|
||||
|
||||
22
bin/options/logger.ts
vendored
Normal file
22
bin/options/logger.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import log from 'loglevel';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const logger = {
|
||||
info(...msg: any[]) {
|
||||
log.info(...msg.map((m) => chalk.blue.bold(m)));
|
||||
},
|
||||
debug(...msg: any[]) {
|
||||
log.debug(...msg);
|
||||
},
|
||||
error(...msg: any[]) {
|
||||
log.error(...msg.map((m) => chalk.red.bold(m)));
|
||||
},
|
||||
warn(...msg: any[]) {
|
||||
log.info(...msg.map((m) => chalk.yellow.bold(m)));
|
||||
},
|
||||
success(...msg: any[]) {
|
||||
log.info(...msg.map((m) => chalk.green.bold(m)));
|
||||
}
|
||||
};
|
||||
|
||||
export default logger;
|
||||
8
bin/utils/dir.ts
vendored
Normal file
8
bin/utils/dir.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
|
||||
export const npmDirectory = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..'
|
||||
);
|
||||
@@ -26,7 +26,7 @@
|
||||
"build": "npm run tauri build -- --target universal-apple-darwin",
|
||||
"build:windows": "npm run tauri build -- --target x86_64-pc-windows-msvc",
|
||||
"build:linux": "npm run tauri build --release",
|
||||
"build:all-unix":"chmod +x ./script/build.sh && ./script/build.sh",
|
||||
"build:all-unix": "chmod +x ./script/build.sh && ./script/build.sh",
|
||||
"build:all-windows": ".\\script\\build.bat",
|
||||
"tauri": "tauri",
|
||||
"cli": "rollup -c rollup.config.js --watch",
|
||||
@@ -40,6 +40,7 @@
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"@tauri-apps/cli": "^1.2.1",
|
||||
"axios": "^1.1.3",
|
||||
"chalk": "^5.1.2",
|
||||
"commander": "^9.4.1",
|
||||
"file-type": "^18.0.0",
|
||||
"is-url": "^1.2.4",
|
||||
@@ -47,7 +48,8 @@
|
||||
"ora": "^6.1.2",
|
||||
"prompts": "^2.4.2",
|
||||
"shelljs": "^0.8.5",
|
||||
"tmp-promise": "^3.0.3"
|
||||
"tmp-promise": "^3.0.3",
|
||||
"update-notifier": "^6.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-alias": "^4.0.2",
|
||||
@@ -60,6 +62,7 @@
|
||||
"@types/prompts": "^2.4.1",
|
||||
"@types/shelljs": "^0.8.11",
|
||||
"@types/tmp": "^0.2.3",
|
||||
"@types/update-notifier": "^6.0.1",
|
||||
"app-root-path": "^3.1.0",
|
||||
"concurrently": "^7.5.0",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
4
rollup.config.js
vendored
4
rollup.config.js
vendored
@@ -4,9 +4,6 @@ import typescript from '@rollup/plugin-typescript';
|
||||
import alias from '@rollup/plugin-alias';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import json from '@rollup/plugin-json';
|
||||
import terser from '@rollup/plugin-terser';
|
||||
|
||||
const isDev = process.env.NODE_ENV !== "production";
|
||||
|
||||
export default {
|
||||
input: 'bin/cli.ts',
|
||||
@@ -23,6 +20,5 @@ export default {
|
||||
alias({
|
||||
entries: [{ find: '@', replacement: path.join(appRootPath.path, 'bin') }],
|
||||
}),
|
||||
!isDev && terser(),
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user