Merge branch 'dev' of codeup.aliyun.com:61c14a7efa282c88e103c23f/oak-cli into dev

This commit is contained in:
Xu Chang 2024-01-05 12:20:40 +08:00
commit 35f4ffacb9
24 changed files with 748 additions and 130 deletions

View File

@ -17,20 +17,24 @@ let moduleFileExtensions = [
'mp.ts',
'ts',
];
if (process.env.NODE_ENV !== 'production' && process.env.PROD !== 'true') {
moduleFileExtensions = [
'dev.mp.js',
'dev.mp.ts',
'dev.js',
'dev.ts',
].concat(moduleFileExtensions);
} else {
if (
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'staging' ||
process.env.PROD === 'true'
) {
moduleFileExtensions = [
'prod.mp.js',
'prod.mp.ts',
'prod.js',
'prod.ts',
].concat(moduleFileExtensions);
} else {
moduleFileExtensions = [
'dev.mp.js',
'dev.mp.ts',
'dev.js',
'dev.ts',
].concat(moduleFileExtensions);
}
// Resolve file paths in the same order as webpack

View File

@ -1,13 +1,48 @@
const { resolve } = require('path');
const watchFolders = process.env.NODE_ENV === 'production' ? ['../src', '../node_modules'] : [
const watchFolders = [
'../src', '../node_modules', '../../oak-domain', '../../oak-common-aspect', '../../oak-external-sdk',
'../../oak-frontend-base', '../../oak-general-business', '../../oak-memory-tree-store'
];
const sourceExts = (process.env.NODE_ENV === 'production' || process.env.PROD === 'true') ?
['prod.ts', 'ts', 'tsx', 'prod.js', 'js', 'jsx', 'less', 'json', 'svg'] :
['dev.ts', 'ts', 'tsx', 'dev.js', 'js', 'jsx', 'less', 'json', 'svg'];
const sourceExts =
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'staging' ||
process.env.PROD === 'true'
? [
'prod.native.js',
'prod.native.ts',
'prod.native.jsx',
'prod.native.tsx',
'prod.js',
'prod.ts',
'prod.jsx',
'prod.tsx',
'js',
'ts',
'jsx',
'tsx',
'less',
'json',
'svg',
]
: [
'dev.native.js',
'dev.native.ts',
'dev.native.jsx',
'dev.native.tsx',
'dev.js',
'dev.ts',
'dev.jsx',
'dev.tsx',
'js',
'ts',
'jsx',
'tsx',
'less',
'json',
'svg',
];
const NullModules = ['fs', 'url'];
/**

View File

@ -40,24 +40,32 @@ let moduleFileExtensions = [
'pc.jsx',
'jsx',
];
if (process.env.NODE_ENV !== 'production' && process.env.PROD !== 'true') {
moduleFileExtensions = [
'dev.web.js',
'dev.web.ts',
'dev.web.tsx',
'dev.js',
'dev.ts',
'dev.tsx',
].concat(moduleFileExtensions);
} else {
if (
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'staging' ||
process.env.PROD === 'true'
) {
moduleFileExtensions = [
'prod.web.js',
'prod.web.ts',
'prod.web.jsx',
'prod.web.tsx',
'prod.js',
'prod.ts',
'prod.jsx',
'prod.tsx',
].concat(moduleFileExtensions);
} else {
moduleFileExtensions = [
'dev.web.js',
'dev.web.ts',
'dev.web.jsx',
'dev.web.tsx',
'dev.js',
'dev.ts',
'dev.jsx',
'dev.tsx',
].concat(moduleFileExtensions);
}
// Resolve file paths in the same order as webpack

View File

@ -8,32 +8,43 @@ const makeLocale_1 = tslib_1.__importDefault(require("./makeLocale"));
const makeRouter_1 = tslib_1.__importDefault(require("./makeRouter"));
const fs_1 = require("fs");
async function build(cmd) {
const mode = (cmd.mode || 'development');
const target = cmd.target;
if (!cmd.target) {
(0, tip_style_1.Error)(`${(0, tip_style_1.error)(`Please add --target web or --target mp(wechatMp) or --target rn(native) to run the project in Web/WechatMp/ReactNative environment`)}`);
return;
}
let subdir = cmd.subDir;
if (!subdir) {
subdir = ['mp', 'wechatMp'].includes(cmd.target) ? 'wechatMp' : (['native', 'rn'].includes(cmd.target) ? 'native' : 'web');
subdir = ['mp', 'wechatMp'].includes(target)
? 'wechatMp'
: ['native', 'rn'].includes(target)
? 'native'
: 'web';
}
// 先makeLocale
(0, makeLocale_1.default)('', cmd.mode === 'development');
(0, makeLocale_1.default)('', mode === 'development');
// 再尝试makeRouter
(0, makeRouter_1.default)({ subdir }, cmd.mode === 'development');
(0, makeRouter_1.default)({ subdir }, mode === 'development');
//ts类型检查 waring 还是error,
//主要web受影响error级别的话 控制台和网页都报错warning级别的话 控制台报错
// development/staging/production
const TSC_COMPILE_ON_ERROR = cmd.check !== 'error';
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`build ${cmd.target} environment:${cmd.mode} ${['development'].includes(cmd.mode) ? `server:${!!cmd.prod}` : ''} ${['mp', 'wechatMp'].includes(cmd.target) &&
['development'].includes(cmd.mode)
const errorLevel = cmd.check !== 'error';
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`build ${target} environment:${mode} ${['development'].includes(mode) ? `server:${!!cmd.prod}` : ''} ${['mp', 'wechatMp'].includes(target) &&
['development'].includes(mode)
? `split:${!!cmd.split}`
: ''}`)}`);
if (['mp', 'wechatMp'].includes(cmd.target)) {
if (['mp', 'wechatMp'].includes(target)) {
const mpFileMap = {
production: 'build-mp.js',
staging: 'build-staging-mp.js',
development: 'start-mp.js',
};
const result = cross_spawn_1.default.sync(`cross-env`, [
`NODE_ENV=${cmd.mode}`,
`NODE_TARGET=${cmd.target}`,
`NODE_ENV=${mode}`,
`NODE_TARGET=${target}`,
`SUB_DIR_NAME=${subdir}`,
`TSC_COMPILE_ON_ERROR=${TSC_COMPILE_ON_ERROR}`,
`TSC_COMPILE_ON_ERROR=${errorLevel}`,
`COMPILE_ANALYZE=${!!cmd.analyze}`,
`GENERATE_SOURCEMAP=${!!cmd.sourcemap}`,
`PROD=${!!cmd.prod}`,
@ -41,9 +52,7 @@ async function build(cmd) {
!!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`,
`node`,
cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`,
require.resolve(`../scripts/${cmd.mode === 'production'
? 'build-mp.js'
: 'start-mp.js'}`),
(0, path_1.resolve)(__dirname, `../scripts/${mpFileMap[mode]}`),
].filter(Boolean), {
stdio: 'inherit',
shell: true,
@ -55,21 +64,24 @@ async function build(cmd) {
(0, tip_style_1.Error)(`${(0, tip_style_1.error)(`执行失败`)}`);
}
}
else if (cmd.target === 'web') {
else if (target === 'web') {
const webFileMap = {
production: 'build-web.js',
staging: 'build-staging-web.js',
development: 'start-web.js',
};
const result = cross_spawn_1.default.sync(`cross-env`, [
`NODE_ENV=${cmd.mode}`,
`NODE_TARGET=${cmd.target}`,
`NODE_ENV=${mode}`,
`NODE_TARGET=${target}`,
`SUB_DIR_NAME=${subdir}`,
`TSC_COMPILE_ON_ERROR=${TSC_COMPILE_ON_ERROR}`,
`TSC_COMPILE_ON_ERROR=${errorLevel}`,
`COMPILE_ANALYZE=${!!cmd.analyze}`,
`GENERATE_SOURCEMAP=${!!cmd.sourcemap}`,
`PROD=${!!cmd.prod}`,
!!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`,
`node`,
cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`,
require.resolve(`../scripts/${cmd.mode === 'production'
? 'build-web.js'
: 'start-web.js'}`),
(0, path_1.resolve)(__dirname, `../scripts/${webFileMap[mode]}`),
].filter(Boolean), {
stdio: 'inherit',
shell: true,
@ -81,33 +93,52 @@ async function build(cmd) {
(0, tip_style_1.Error)(`${(0, tip_style_1.error)(`执行失败`)}`);
}
}
else if (['native', 'rn'].includes(cmd.target)) {
else if (['native', 'rn'].includes(target)) {
const prjDir = process.cwd();
const cwd = (0, path_1.resolve)(prjDir, subdir);
(0, fs_1.copyFileSync)((0, path_1.resolve)(prjDir, 'package.json'), (0, path_1.resolve)(cwd, 'package.json'));
// rn不支持注入NODE_ENVIRONMENT这样的环境变量cross-env没有用
/* const result = spawn.sync(
'react-native',
[
'start',
],
{
const platform = cmd.platform;
let result;
if (mode === 'production') {
//cd native/android && cross-env NODE_ENV=production ./gradlew assembleRelease
result = cross_spawn_1.default.sync(`cd android`, [
'&& cross-env',
`NODE_ENV=${mode}`,
'OAK_PLATFORM=native',
'./gradlew assembleRelease',
].filter(Boolean), {
cwd,
stdio: 'inherit',
shell: true,
}
); */
const result = cross_spawn_1.default.sync(`cross-env`, [
`NODE_ENV=${cmd.mode}`,
'OAK_PLATFORM=native',
`PROD=${!!cmd.prod}`,
'react-native',
'start',
].filter(Boolean), {
cwd,
stdio: 'inherit',
shell: true,
});
});
}
else if (mode === 'staging') {
//cd native/android && cross-env NODE_ENV=production ./gradlew assembleStaging
result = cross_spawn_1.default.sync(`cd android`, [
'&& cross-env',
`NODE_ENV=${mode}`,
'OAK_PLATFORM=native',
'./gradlew assembleStaging',
].filter(Boolean), {
cwd,
stdio: 'inherit',
shell: true,
});
}
else {
result = cross_spawn_1.default.sync(`cross-env`, [
`NODE_ENV=${mode}`,
'OAK_PLATFORM=native',
`PROD=${!!cmd.prod}`,
'react-native',
'start',
].filter(Boolean), {
cwd,
stdio: 'inherit',
shell: true,
});
}
if (result.status === 0) {
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`执行完成`)}`);
}

1
lib/clean.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export default function run(options: any): Promise<void>;

51
lib/clean.js Normal file
View File

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const tip_style_1 = require("./tip-style");
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
const path_1 = require("path");
const fs_1 = require("fs");
async function run(options) {
const prjDir = process.cwd();
const cwd = (0, path_1.resolve)(process.cwd(), options.subDir || 'native');
const mode = options.mode || 'development'; //development/staging/production
if (options.platform === 'ios') {
(0, fs_1.copyFileSync)((0, path_1.resolve)(prjDir, 'package.json'), (0, path_1.resolve)(cwd, 'package.json'));
(0, tip_style_1.Success)(`${(0, tip_style_1.primary)('run react-native run-ios')}`);
const result = cross_spawn_1.default.sync('react-native', ['run-ios'], {
cwd,
stdio: 'inherit',
shell: true,
});
if (result.status === 0) {
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`react-native run-ios success`)}`);
}
else {
Error(`${(0, tip_style_1.error)('react-native run-ios fail')}`);
process.exit(-1);
}
}
else if (options.platform === 'android') {
(0, tip_style_1.Success)(`${(0, tip_style_1.primary)('run react-native run-android')}`);
(0, fs_1.copyFileSync)((0, path_1.resolve)(prjDir, 'package.json'), (0, path_1.resolve)(cwd, 'package.json'));
const result = cross_spawn_1.default.sync('cd android', [
'&& ./gradlew clean',
].filter(Boolean), {
cwd,
stdio: 'inherit',
shell: true,
});
if (result.status === 0) {
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`react-native run-android success`)}`);
}
else {
Error(`${(0, tip_style_1.error)('react-native run-android fail')}`);
process.exit(-1);
}
}
else {
Error((0, tip_style_1.error)(`unrecoganized platfrom: ${options.platform}`));
process.exit(-1);
}
}
exports.default = run;

View File

@ -8,6 +8,7 @@ const build_1 = tslib_1.__importDefault(require("./build"));
const makeDomain_1 = tslib_1.__importDefault(require("./makeDomain"));
const makeLocale_1 = tslib_1.__importDefault(require("./makeLocale"));
const run_1 = tslib_1.__importDefault(require("./run"));
const clean_1 = tslib_1.__importDefault(require("./clean"));
const config_1 = require("./config");
const tip_style_1 = require("./tip-style");
/**
@ -71,6 +72,7 @@ commander_1.default
.option('-m, --mode <mode>', 'mode')
.option('-d, --subDir <subDirName>', 'subDirName')
.option('-c, --check <level>', 'level')
.option('-p, --platform <platform>', 'platform')
.description('build project of build on demand')
.action(build_1.default);
commander_1.default
@ -89,8 +91,15 @@ commander_1.default
.command('run')
.option('-p, --platform <platform>', 'platform')
.option('-d, --subDir <subDirName>', 'subDirName')
.option('-m, --mode <mode>', 'mode')
.description(`run backend server by ${config_1.CLI_NAME}`)
.action(run_1.default);
commander_1.default
.command('clean')
.option('-p, --platform <platform>', 'platform')
.option('-d, --subDir <subDirName>', 'subDirName')
.description(`clean rn build by ${config_1.CLI_NAME}`)
.action(clean_1.default);
// output help information on unknown commands
commander_1.default.arguments('<command>').action((cmd) => {
commander_1.default.outputHelp();

View File

@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const tip_style_1 = require("./tip-style");
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
const path_1 = require("path");
async function make() {
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`make oak-app-domain`)}`);
// ts-node scripts/build-app-domain & npm link ./app-domain
const result = cross_spawn_1.default.sync('ts-node', [require.resolve('../scripts/' + 'make-app-domain.js')], {
const result = cross_spawn_1.default.sync('ts-node', [(0, path_1.resolve)(__dirname, '../scripts/make-app-domain.js')], {
stdio: 'inherit',
shell: true,
});

View File

@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const tip_style_1 = require("./tip-style");
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
const path_1 = require("path");
async function make(cmd, watch) {
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`make locales`)}`);
// ts-node scripts/build-app-domain & npm link ./app-domain
const args = [require.resolve('../scripts/' + 'make-locale.js')];
const args = [(0, path_1.resolve)(__dirname, '../scripts/make-locale.js')];
if (watch) {
args.push('true');
}

View File

@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const tip_style_1 = require("./tip-style");
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
const path_1 = require("path");
async function make(cmd, watch) {
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`make router`)}`);
// node scripts/make-router.js subdir watch
const args = [require.resolve('../scripts/' + 'make-router.js'), cmd.subdir];
const args = [(0, path_1.resolve)(__dirname, '../scripts/make-router.js'), cmd.subdir];
if (watch) {
args.push('true');
const result = (0, cross_spawn_1.default)('node', args, {

View File

@ -8,6 +8,7 @@ const fs_1 = require("fs");
async function run(options) {
const prjDir = process.cwd();
const cwd = (0, path_1.resolve)(process.cwd(), options.subDir || 'native');
const mode = options.mode || 'development'; //development/staging/production
if (options.platform === 'ios') {
(0, fs_1.copyFileSync)((0, path_1.resolve)(prjDir, 'package.json'), (0, path_1.resolve)(cwd, 'package.json'));
(0, tip_style_1.Success)(`${(0, tip_style_1.primary)('run react-native run-ios')}`);
@ -27,7 +28,12 @@ async function run(options) {
else if (options.platform === 'android') {
(0, tip_style_1.Success)(`${(0, tip_style_1.primary)('run react-native run-android')}`);
(0, fs_1.copyFileSync)((0, path_1.resolve)(prjDir, 'package.json'), (0, path_1.resolve)(cwd, 'package.json'));
const result = cross_spawn_1.default.sync('react-native', ['run-android'], {
const result = cross_spawn_1.default.sync('cross-env', [
`NODE_ENV=${mode}`,
'react-native',
'run-android',
mode === 'production' ? '--variant=release' : '',
].filter(Boolean), {
cwd,
stdio: 'inherit',
shell: true,

View File

@ -53,12 +53,16 @@ function packageJsonContent({ name, version, description, cliName, cliBinName, i
"copy-config-json": "copyfiles -u 1 src/config/*.json lib/",
"start:mp": "${cliBinName} start --target mp --mode development",
"start:mp:prod": "${cliBinName} start --target mp --mode development --prod",
"build:mp:staging": "${cliBinName} build --target mp --mode staging",
"build-analyze:mp:staging": "${cliBinName} build --target mp --mode staging --analyze",
"build:mp": "${cliBinName} build --target mp --mode production",
"build-analyze:mp": "${cliBinName} build --target mp --mode production --analyze",
"start:web": "${cliBinName} start --target web --mode development",
"start:web:prod": "${cliBinName} start --target web --mode development --prod",
"start:native": "${cliBinName} start --target rn --mode development",
"start:native:prod": "${cliBinName} start --target rn --mode development --prod",
"build:web:staging": "${cliBinName} build --target web --mode staging",
"build-analyze:web:staging": "${cliBinName} build --target web --mode staging --analyze",
"build:web": "${cliBinName} build --target web --mode production",
"build-analyze:web": "${cliBinName} build --target web --mode production --analyze",
"build-sourcemap-analyze:web": "${cliBinName} build --target web --mode production --sourcemap --analyze",

View File

@ -0,0 +1,92 @@
'use strict';
const { ProvidePlugin } = require('webpack');
const filterObject = require('filter-obj');
function createAliasFilter({ includeAliases, excludeAliases }) {
if (includeAliases.length > 0) {
return (object) =>
filterObject(object, (key) => includeAliases.includes(key));
}
return (object) =>
filterObject(object, (key) => !excludeAliases.includes(key));
}
module.exports = class NodePolyfillPlugin {
constructor(options = {}) {
this.options = {
excludeAliases: [],
includeAliases: [],
...options,
};
if (
this.options.includeAliases.length > 0 &&
this.options.excludeAliases.length > 0
) {
throw new Error(
'excludeAliases and includeAliases are mutually exclusive'
);
}
}
apply(compiler) {
const filter = createAliasFilter(this.options);
compiler.options.plugins.push(
new ProvidePlugin(
filter({
Buffer: [require.resolve('buffer/'), 'Buffer'],
console: require.resolve('console-browserify'),
process: require.resolve('process/browser'),
})
)
);
compiler.options.resolve.fallback = {
...filter({
assert: require.resolve('assert/'),
buffer: require.resolve('buffer/'),
console: require.resolve('console-browserify'),
constants: require.resolve('constants-browserify'),
crypto: require.resolve('crypto-browserify'),
domain: require.resolve('domain-browser'),
events: require.resolve('events/'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
os: require.resolve('os-browserify/browser'),
path: require.resolve('path-browserify'),
punycode: require.resolve('punycode/'),
process: require.resolve('process/browser'),
querystring: require.resolve('querystring-es3'),
stream: require.resolve('stream-browserify'),
/* eslint-disable camelcase */
_stream_duplex: require.resolve(
'readable-stream/lib/_stream_duplex'
),
_stream_passthrough: require.resolve(
'readable-stream/lib/_stream_passthrough'
),
_stream_readable: require.resolve(
'readable-stream/lib/_stream_readable'
),
_stream_transform: require.resolve(
'readable-stream/lib/_stream_transform'
),
_stream_writable: require.resolve(
'readable-stream/lib/_stream_writable'
),
string_decoder: require.resolve('string_decoder/'),
/* eslint-enable camelcase */
sys: require.resolve('util/'),
timers: require.resolve('timers-browserify'),
tty: require.resolve('tty-browserify'),
url: require.resolve('url/'),
util: require.resolve('util/'),
vm: require.resolve('vm-browserify'),
zlib: require.resolve('browserify-zlib'),
}),
...compiler.options.resolve.fallback,
};
}
};

View File

@ -1,3 +1,6 @@
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
require('../config/mp/env');
const webpack = require('webpack');

View File

@ -0,0 +1,35 @@
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'staging';
require('../config/mp/env');
const webpack = require('webpack');
const chalk = require('chalk');
const fs = require('fs-extra');
const configFactory = require('../config/mp/webpack.config');
const config = configFactory('production');
const paths = require('../config/mp/paths');
const getClientEnvironment = require('../config/mp/env');
const env = getClientEnvironment();
fs.emptyDirSync(paths.appBuild);
webpack(config, (err, stats) => {
if (err) {
console.log(chalk.red(err.stack || err));
if (err.details) {
console.log(chalk.red(err.details));
}
return undefined;
}
if (stats) {
const info = stats.toJson();
if (stats.hasErrors()) {
info.errors.forEach((ele) => console.warn(ele));
}
if (stats.hasWarnings()) {
info.warnings.forEach((ele) => console.warn(ele));
}
}
});

View File

@ -0,0 +1,217 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'staging';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/web/env');
const path = require('path');
const chalk = require('react-dev-utils/chalk');
const fs = require('fs-extra');
const bfj = require('bfj');
const webpack = require('webpack');
const configFactory = require('../config/web/webpack.config');
const paths = require('../config/web/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
const argv = process.argv.slice(2);
const writeStatsJson = argv.indexOf('--stats') !== -1;
// Generate configuration
const config = configFactory('production');
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
return measureFileSizesBeforeBuild(paths.appBuild);
})
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' +
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrlOrPath;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
err => {
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
if (tscCompileOnError) {
console.log(
chalk.yellow(
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
)
);
printBuildError(err);
} else {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
}
)
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
console.log('Creating an optimized production build...');
const compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
if (err) {
if (!err.message) {
return reject(err);
}
let errMessage = err.message;
// Add additional information for postcss errors
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
errMessage +=
'\nCompileError: Begins at CSS selector ' +
err['postcssNode'].selector;
}
messages = formatWebpackMessages({
errors: [errMessage],
warnings: [],
});
} else {
messages = formatWebpackMessages(
stats.toJson({ all: false, warnings: true, errors: true })
);
}
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join('\n\n')));
}
if (
process.env.CI &&
(typeof process.env.CI !== 'string' ||
process.env.CI.toLowerCase() !== 'false') &&
messages.warnings.length
) {
// Ignore sourcemap warnings in CI builds. See #8227 for more info.
const filteredWarnings = messages.warnings.filter(
w => !/Failed to parse source map/.test(w)
);
if (filteredWarnings.length) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
)
);
return reject(new Error(filteredWarnings.join('\n\n')));
}
}
const resolveArgs = {
stats,
previousFileSizes,
warnings: messages.warnings,
};
if (writeStatsJson) {
return bfj
.write(paths.appBuild + '/bundle-stats.json', stats.toJson())
.then(() => resolve(resolveArgs))
.catch(error => reject(new Error(error)));
}
return resolve(resolveArgs);
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: (file) => file !== paths.appHtml,
});
}

View File

@ -13,7 +13,12 @@ import makeLocale from './makeLocale';
import makeRouter from './makeRouter';
import { copyFileSync } from 'fs';
type Mode = 'development' | 'staging' | 'production';
type Target = 'mp' | 'wechatMp' | 'web' | 'rn' | 'native';
export default async function build(cmd: any) {
const mode = (cmd.mode || 'development') as Mode;
const target = cmd.target as Target;
if (!cmd.target) {
Error(
`${error(
@ -24,38 +29,45 @@ export default async function build(cmd: any) {
}
let subdir = cmd.subDir;
if (!subdir) {
subdir = ['mp', 'wechatMp'].includes(cmd.target) ? 'wechatMp' : (
['native', 'rn'].includes(cmd.target) ? 'native' : 'web'
);
subdir = ['mp', 'wechatMp'].includes(target)
? 'wechatMp'
: ['native', 'rn'].includes(target)
? 'native'
: 'web';
}
// 先makeLocale
makeLocale('', cmd.mode === 'development');
makeLocale('', mode === 'development');
// 再尝试makeRouter
makeRouter({ subdir }, cmd.mode === 'development');
makeRouter({ subdir }, mode === 'development');
//ts类型检查 waring 还是error,
//主要web受影响error级别的话 控制台和网页都报错warning级别的话 控制台报错
// development/staging/production
const TSC_COMPILE_ON_ERROR = cmd.check !== 'error';
const errorLevel = cmd.check !== 'error';
Success(
`${success(
`build ${cmd.target} environment:${cmd.mode} ${
['development'].includes(cmd.mode) ? `server:${!!cmd.prod}` : ''
`build ${target} environment:${mode} ${
['development'].includes(mode) ? `server:${!!cmd.prod}` : ''
} ${
['mp', 'wechatMp'].includes(cmd.target) &&
['development'].includes(cmd.mode)
['mp', 'wechatMp'].includes(target) &&
['development'].includes(mode)
? `split:${!!cmd.split}`
: ''
}`
)}`
);
if (['mp', 'wechatMp'].includes(cmd.target)) {
if (['mp', 'wechatMp'].includes(target)) {
const mpFileMap = {
production: 'build-mp.js',
staging: 'build-staging-mp.js',
development: 'start-mp.js',
};
const result = spawn.sync(
`cross-env`,
[
`NODE_ENV=${cmd.mode}`,
`NODE_TARGET=${cmd.target}`,
`NODE_ENV=${mode}`,
`NODE_TARGET=${target}`,
`SUB_DIR_NAME=${subdir}`,
`TSC_COMPILE_ON_ERROR=${TSC_COMPILE_ON_ERROR}`,
`TSC_COMPILE_ON_ERROR=${errorLevel}`,
`COMPILE_ANALYZE=${!!cmd.analyze}`,
`GENERATE_SOURCEMAP=${!!cmd.sourcemap}`,
`PROD=${!!cmd.prod}`,
@ -63,13 +75,7 @@ export default async function build(cmd: any) {
!!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`,
`node`,
cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`,
require.resolve(
`../scripts/${
cmd.mode === 'production'
? 'build-mp.js'
: 'start-mp.js'
}`
),
resolve(__dirname, `../scripts/${mpFileMap[mode]}`),
].filter(Boolean),
{
stdio: 'inherit',
@ -81,27 +87,26 @@ export default async function build(cmd: any) {
} else {
Error(`${error(`执行失败`)}`);
}
} else if (cmd.target === 'web') {
} else if (target === 'web') {
const webFileMap = {
production: 'build-web.js',
staging: 'build-staging-web.js',
development: 'start-web.js',
};
const result = spawn.sync(
`cross-env`,
[
`NODE_ENV=${cmd.mode}`,
`NODE_TARGET=${cmd.target}`,
`NODE_ENV=${mode}`,
`NODE_TARGET=${target}`,
`SUB_DIR_NAME=${subdir}`,
`TSC_COMPILE_ON_ERROR=${TSC_COMPILE_ON_ERROR}`,
`TSC_COMPILE_ON_ERROR=${errorLevel}`,
`COMPILE_ANALYZE=${!!cmd.analyze}`,
`GENERATE_SOURCEMAP=${!!cmd.sourcemap}`,
`PROD=${!!cmd.prod}`,
!!cmd.memoryLimit && `MEMORY_LIMIT=${cmd.memoryLimit}`,
`node`,
cmd.memoryLimit && `--max_old_space_size=${cmd.memoryLimit}`,
require.resolve(
`../scripts/${
cmd.mode === 'production'
? 'build-web.js'
: 'start-web.js'
}`
),
resolve(__dirname, `../scripts/${webFileMap[mode]}`),
].filter(Boolean),
{
stdio: 'inherit',
@ -113,7 +118,7 @@ export default async function build(cmd: any) {
} else {
Error(`${error(`执行失败`)}`);
}
} else if (['native', 'rn'].includes(cmd.target)) {
} else if (['native', 'rn'].includes(target)) {
const prjDir = process.cwd();
const cwd = resolve(prjDir, subdir);
copyFileSync(
@ -121,32 +126,58 @@ export default async function build(cmd: any) {
resolve(cwd, 'package.json')
);
// rn不支持注入NODE_ENVIRONMENT这样的环境变量cross-env没有用
/* const result = spawn.sync(
'react-native',
[
'start',
],
{
cwd,
stdio: 'inherit',
shell: true,
}
); */
const result = spawn.sync(
`cross-env`,
[
`NODE_ENV=${cmd.mode}`,
'OAK_PLATFORM=native',
`PROD=${!!cmd.prod}`,
'react-native',
'start',
].filter(Boolean),
{
cwd,
stdio: 'inherit',
shell: true,
}
);
const platform = cmd.platform as 'ios' | 'android';
let result;
if (mode === 'production') {
//cd native/android && cross-env NODE_ENV=production ./gradlew assembleRelease
result = spawn.sync(
`cd android`,
[
'&& cross-env',
`NODE_ENV=${mode}`,
'OAK_PLATFORM=native',
'./gradlew assembleRelease',
].filter(Boolean),
{
cwd,
stdio: 'inherit',
shell: true,
}
);
} else if (mode === 'staging') {
//cd native/android && cross-env NODE_ENV=production ./gradlew assembleStaging
result = spawn.sync(
`cd android`,
[
'&& cross-env',
`NODE_ENV=${mode}`,
'OAK_PLATFORM=native',
'./gradlew assembleStaging',
].filter(Boolean),
{
cwd,
stdio: 'inherit',
shell: true,
}
);
} else {
result = spawn.sync(
`cross-env`,
[
`NODE_ENV=${mode}`,
'OAK_PLATFORM=native',
`PROD=${!!cmd.prod}`,
'react-native',
'start',
].filter(Boolean),
{
cwd,
stdio: 'inherit',
shell: true,
}
);
}
if (result.status === 0) {
Success(`${success(`执行完成`)}`);
} else {

66
src/clean.ts Normal file
View File

@ -0,0 +1,66 @@
import {
Success,
error,
primary,
success,
warn,
Warn,
} from './tip-style';
import spawn from 'cross-spawn';
import { resolve } from 'path';
import { copyFileSync, unlinkSync } from 'fs';
export default async function run(options: any): Promise<void> {
const prjDir = process.cwd();
const cwd = resolve(process.cwd(), options.subDir || 'native');
const mode = options.mode || 'development'; //development/staging/production
if (options.platform === 'ios') {
copyFileSync(resolve(prjDir, 'package.json'), resolve(cwd, 'package.json'));
Success(`${primary('run react-native run-ios')}`);
const result = spawn.sync(
'react-native',
['run-ios'],
{
cwd,
stdio: 'inherit',
shell: true,
}
);
if (result.status === 0) {
Success(`${success(`react-native run-ios success`)}`);
} else {
Error(`${error('react-native run-ios fail')}`);
process.exit(-1);
}
}
else if (options.platform === 'android') {
Success(`${primary('run react-native run-android')}`);
copyFileSync(
resolve(prjDir, 'package.json'),
resolve(cwd, 'package.json')
);
const result = spawn.sync(
'cd android',
[
'&& ./gradlew clean',
].filter(Boolean),
{
cwd,
stdio: 'inherit',
shell: true,
}
);
if (result.status === 0) {
Success(`${success(`react-native run-android success`)}`);
} else {
Error(`${error('react-native run-android fail')}`);
process.exit(-1);
}
}
else {
Error(error(`unrecoganized platfrom: ${options.platform}`));
process.exit(-1);
}
}

View File

@ -5,6 +5,7 @@ import build from './build';
import makeDomain from './makeDomain';
import makeLocale from './makeLocale';
import run from './run';
import clean from './clean';
import { CLI_VERSION, CLI_NAME } from './config';
import { error, warn } from './tip-style';
@ -77,6 +78,7 @@ program
.option('-m, --mode <mode>', 'mode')
.option('-d, --subDir <subDirName>', 'subDirName')
.option('-c, --check <level>', 'level')
.option('-p, --platform <platform>', 'platform')
.description('build project of build on demand')
.action(build);
program
@ -95,8 +97,15 @@ program
.command('run')
.option('-p, --platform <platform>', 'platform')
.option('-d, --subDir <subDirName>', 'subDirName')
.option('-m, --mode <mode>', 'mode')
.description(`run backend server by ${CLI_NAME}`)
.action(run);
program
.command('clean')
.option('-p, --platform <platform>', 'platform')
.option('-d, --subDir <subDirName>', 'subDirName')
.description(`clean rn build by ${CLI_NAME}`)
.action(clean);
// output help information on unknown commands
program.arguments('<command>').action((cmd) => {

View File

@ -8,13 +8,14 @@ import {
Warn,
} from './tip-style';
import spawn from 'cross-spawn';
import { resolve } from 'path';
export default async function make() {
Success(`${success(`make oak-app-domain`)}`);
// ts-node scripts/build-app-domain & npm link ./app-domain
const result = spawn.sync(
'ts-node',
[require.resolve('../scripts/' + 'make-app-domain.js')],
[resolve(__dirname, '../scripts/make-app-domain.js')],
{
stdio: 'inherit',
shell: true,

View File

@ -8,11 +8,12 @@ import {
Warn,
} from './tip-style';
import spawn from 'cross-spawn';
import { resolve } from 'path';
export default async function make(cmd: any, watch?: boolean) {
Success(`${success(`make locales`)}`);
// ts-node scripts/build-app-domain & npm link ./app-domain
const args = [require.resolve('../scripts/' + 'make-locale.js')];
const args = [resolve(__dirname, '../scripts/make-locale.js')];
if (watch) {
args.push('true');
}

View File

@ -8,11 +8,12 @@ import {
Warn,
} from './tip-style';
import spawn from 'cross-spawn';
import { resolve } from 'path';
export default async function make(cmd: any, watch?: boolean) {
Success(`${success(`make router`)}`);
// node scripts/make-router.js subdir watch
const args = [require.resolve('../scripts/' + 'make-router.js'), cmd.subdir];
const args = [resolve(__dirname, '../scripts/make-router.js'), cmd.subdir];
if (watch) {
args.push('true');
const result = spawn(

View File

@ -13,6 +13,7 @@ import { copyFileSync, unlinkSync } from 'fs';
export default async function run(options: any): Promise<void> {
const prjDir = process.cwd();
const cwd = resolve(process.cwd(), options.subDir || 'native');
const mode = options.mode || 'development'; //development/staging/production
if (options.platform === 'ios') {
copyFileSync(resolve(prjDir, 'package.json'), resolve(cwd, 'package.json'));
Success(`${primary('run react-native run-ios')}`);
@ -37,8 +38,13 @@ export default async function run(options: any): Promise<void> {
Success(`${primary('run react-native run-android')}`);
copyFileSync(resolve(prjDir, 'package.json'), resolve(cwd, 'package.json'));
const result = spawn.sync(
'react-native',
['run-android'],
'cross-env',
[
`NODE_ENV=${mode}`,
'react-native',
'run-android',
mode === 'production' ? '--variant=release' : '',
].filter(Boolean),
{
cwd,
stdio: 'inherit',

View File

@ -62,12 +62,16 @@ export function packageJsonContent({
"copy-config-json": "copyfiles -u 1 src/config/*.json lib/",
"start:mp": "${cliBinName} start --target mp --mode development",
"start:mp:prod": "${cliBinName} start --target mp --mode development --prod",
"build:mp:staging": "${cliBinName} build --target mp --mode staging",
"build-analyze:mp:staging": "${cliBinName} build --target mp --mode staging --analyze",
"build:mp": "${cliBinName} build --target mp --mode production",
"build-analyze:mp": "${cliBinName} build --target mp --mode production --analyze",
"start:web": "${cliBinName} start --target web --mode development",
"start:web:prod": "${cliBinName} start --target web --mode development --prod",
"start:native": "${cliBinName} start --target rn --mode development",
"start:native:prod": "${cliBinName} start --target rn --mode development --prod",
"build:web:staging": "${cliBinName} build --target web --mode staging",
"build-analyze:web:staging": "${cliBinName} build --target web --mode staging --analyze",
"build:web": "${cliBinName} build --target web --mode production",
"build-analyze:web": "${cliBinName} build --target web --mode production --analyze",
"build-sourcemap-analyze:web": "${cliBinName} build --target web --mode production --sourcemap --analyze",