// ref:
// - https://umijs.org/plugin/develop.html
import { events, ConfigParser } from 'cordova-common';
import { join } from 'path';
import os from 'os';
import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs-extra';
import childProcess from 'child_process';
import create from './create-cordova';
function getIpAddress() {
const networkInterfaces = os.networkInterfaces();
let ipAddress = '127.0.0.1';
// eslint-disable-next-line no-restricted-syntax
for (const key in networkInterfaces) {
if (networkInterfaces.hasOwnProperty(key)) {
const iface = networkInterfaces[key];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < iface.length; i++) {
const alias = iface[i];
if (
alias.family === 'IPv4' &&
alias.address !== '127.0.0.1' &&
!alias.address.startsWith('169.254') &&
!alias.internal
) {
ipAddress = alias.address;
}
}
}
}
return ipAddress;
}
function setCordovaConfig(path, isProduction) {
const webPort = process.env.PORT || 8000;
const ip = getIpAddress();
const webUrl = !isProduction ? `http://${ip}:${webPort}` : 'index.html';
const configPath = join(path, 'config.xml');
let content = readFileSync(configPath).toString();
const contentPattern = '';
const contentRegex = new RegExp(contentPattern);
content = content.replace(contentRegex, ``);
if (!isProduction) {
const navPattern = '()';
const navRegex = new RegExp(navPattern);
if (navRegex.test(content)) {
content = content.replace(navRegex, `$1href="${webUrl}"$2`);
} else {
const widgePattern = '';
const widgeRegex = new RegExp(widgePattern);
content = content.replace(widgeRegex, `\t\n`);
}
}
writeFileSync(configPath, content);
}
// android 支持 view port
export function supportViewPortForAndroid(path) {
const configPath = join(path, 'config.xml');
const config = new ConfigParser(configPath);
const packageName = config.packageName();
const paths = packageName.split('.');
const mainActivityPath = join(
path,
'platforms/android/app/src/main/java',
...paths,
'MainActivity.java',
);
if (!existsSync(mainActivityPath)) return;
let content = readFileSync(mainActivityPath).toString();
if (!/WebView webView = \(WebView\)[\s]?this\.appView\.getView\(\);/.test(content)) {
content = content.replace(
'loadUrl(launchUrl);',
'loadUrl(launchUrl);\n\t//下面能让 Android 设备支持 viewport\n\tWebView webView = (WebView) this.appView.getView();\n\twebView.getSettings().setLoadWithOverviewMode(true);\n\twebView.getSettings().setUseWideViewPort(true);',
);
writeFileSync(mainActivityPath, content);
}
if (!/import android.webkit.*;/.test(content)) {
content = content.replace('import android.os.Bundle;','import android.os.Bundle;\nimport android.webkit.*;');
writeFileSync(mainActivityPath, content);
}
}
// 修复ios外层滚动
function fixScrollIssueForIOS(path) {
const configPath = join(path, 'config.xml');
const config = new ConfigParser(configPath);
const appName = config.name();
const mainViewControllerPath = join(
path,
'platforms/ios/',
appName,
'Classes/MainViewController.m',
);
if (!existsSync(mainViewControllerPath)) return;
let content = readFileSync(mainViewControllerPath).toString();
if (!/self\.webView\.scrollView\.bounces[\s]?=[\s]?NO;/.test(content)) {
content = content.replace(
'[super viewDidLoad];',
'[super viewDidLoad];\n\tself.webView.scrollView.bounces = NO;\n\tself.webView.scrollView.scrollEnabled = NO;',
);
writeFileSync(mainViewControllerPath, content);
}
}
export default function (api) {
const isProduction = process.env.NODE_ENV === 'production';
const cordovaPlatform = process.env.CORDOVA || 'ios';
const isAlita = process.env.IS_ALITA && process.env.IS_ALITA !== 'none';
api.addHTMLMeta(memo => {
const addItem = [
{
content: 'no',
name: 'msapplication-tap-highlight',
},
];
return [...addItem, ...memo];
});
api.modifyDefaultConfig(memo => {
return {
// build目录默认为www
...memo,
outputPath: 'www',
base: './',
publicPath: './',
history: 'hash',
};
});
// dev
// 1.cordova create
api.registerCommand(
'cordova',
{
description: 'cordova init',
},
args => {
const addPlatforms = (isIos: boolean) => {
childProcess.exec(
`cordova platforms add ${isIos ? 'ios' : 'android'}`,
{},
(error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
} else if (!isIos) {
supportViewPortForAndroid(api.paths.cwd);
} else {
fixScrollIssueForIOS(api.paths.cwd);
}
console.log(stdout);
console.log(stderr);
},
);
console.log(`cordova add ${isIos ? 'ios' : 'android'} platforms ...`);
};
if (args.init) {
// eslint-disable-next-line global-require
// eslint-disable-next-line import/no-dynamic-require
const pkg = require(join(api.paths.cwd, 'package.json'));
const optionalName = pkg.name || 'alitaapp';
const optionalId = `com.alita.${optionalName}`;
create(api.paths.cwd, optionalId, optionalName, {}, events);
if (args.ios || args.android) {
addPlatforms(args.ios);
} else {
console.log(
`cordova init success,please run "${isAlita ? 'alita' : 'umi'} cordova --ios" or "${
isAlita ? 'alita' : 'umi'
} cordova --android" to add cordova platforms`,
);
}
} else if (args.ios || args.android) {
addPlatforms(args.ios);
}
},
);
if (!(process.env.ALITA_NOW_COMMAND === 'dev' || process.env.ALITA_NOW_COMMAND === 'build')) {
return;
}
const configPath = join(api.paths.cwd, 'config.xml');
const platformsPath = join(api.paths.cwd, 'platforms');
if (
existsSync(configPath) &&
existsSync(platformsPath) &&
readdirSync(platformsPath).length > 0
) {
console.log(`cordova platform use ${cordovaPlatform}`);
// 3.node config-xml.js true
// console.log(api);
setCordovaConfig(api.paths.cwd, isProduction);
// 4.cordova build ios
// api.devServerPort 需要提交PR来支持
childProcess.exec(`cordova build ${cordovaPlatform}`, {}, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
}
console.log(stdout);
console.log(stderr);
});
// 5.node serve-cordova.js ios
const dirToServe = join(api.paths.cwd, 'platforms', cordovaPlatform, 'platform_www');
const servePort = 8723;
const serveProcess = childProcess.exec(
`serve -l ${servePort}`,
{ stdio: 'inherit', cwd: dirToServe } as any,
(error, stdout) => {
console.error(error.message);
console.log(stdout.toString('utf8'));
},
);
console.log(`cordova serve(pid:${serveProcess.pid})`);
// 6.add app.js
// export function render(oldRender) {
// function onDeviceReady() {
// oldRender();
// }
// document.addEventListener('deviceready', onDeviceReady, false);
// }
api.addRuntimePlugin(join(__dirname, './runtime'));
// 7.add cordova.js
// <% if(context.env === 'production') { %>
//
// <% } else {%>
//
// <% } %>
const ip = getIpAddress();
let cordovaSrc = './cordova.js';
if (!isProduction) {
cordovaSrc = `http://${ip}:${servePort}/cordova.js`;
}
api.addHTMLScript({
src: cordovaSrc,
});
// 8.umi dev
// build
// 1. outputPath:'www',
// 2. umi build
api.onBuildSuccess(() => {
console.log(`[${isAlita ? 'alita' : 'umi'}]: success`);
console.log(`[${isAlita ? 'alita' : 'umi'}]: run build cordova ...`);
// 3. node config-xml.js false
setCordovaConfig(api.paths.cwd, isProduction);
// 4. cordova build ios
childProcess.exec(`cordova build ${cordovaPlatform}`, {}, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
}
console.log(stdout);
console.log(stderr);
process.exit();
});
});
} else {
console.log(
`please run "${
isAlita ? 'alita' : 'umi'
} cordova --init --ios" to init cordova and add cordova platform`,
);
}
}