You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
if (isMiniAppTargeted) {
builtInPlugins.push('build-plugin-rax-miniapp');
}
if (userConfig.web) {
if (userConfig.web.pha) {
builtInPlugins.push('build-plugin-rax-pha');
}
// Make ssr plugin after base plugin which need registerTask, the action will override the devServer config
if (userConfig.web.ssr) {
builtInPlugins.push('build-plugin-ssr');
}
}
if (router) {
builtInPlugins.push('build-plugin-rax-router');
}
builtInPlugins.push('build-plugin-ice-logger');
if (experiments.minifyCSSModules === true) {
builtInPlugins.push('build-plugin-minify-classname');
}
watcher.on('change', function() {
console.log('\n');
log.info('build.json has been changed');
log.info('restart dev server');
// add process env for mark restart dev process
process.env.RESTART_DEV = true;
child.kill();
restartProcess(forkChildProcessPath);
});
for (const item of configArr) {
const { chainConfig } = item;
const config = chainConfig.toConfig() as WebpackOptionsNormalized;
if (config.devServer) {
devServerConfig = deepmerge(devServerConfig, config.devServer);
}
// if --port or process.env.PORT has been set, overwrite option port
if (process.env.USE_CLI_PORT) {
devServerConfig.port = commandArgs.port;
}
}
//rax-app
#!/usr/bin/env node
const utils = require('create-cli-utils');
const packageInfo = require('../package.json');
const getBuiltInPlugins = require('../lib');
const forkChildProcessPath = require.resolve('./child-process-start');
(async () => {
//packageInfo rax-app pkg.json
//getBuiltInPlugins rax-app 自己需要的webpack配置
//forkChildProcessPath 和getBuiltInPlugins绑定
await utils.createCli(getBuiltInPlugins, forkChildProcessPath, packageInfo );
})();
#!/usr/bin/env node
const { childProcessStart } = require('create-cli-utils');
const getBuiltInPlugins = require('../lib');
(async () => {
await childProcessStart(getBuiltInPlugins);
})();
import { IGetBuiltInPlugins, IPluginList, Json, IUserConfig } from 'build-scripts';
import * as miniappBuilderShared from 'miniapp-builder-shared';
import { init } from '@builder/pack/deps/webpack/webpack';
import { hijackWebpack } from './require-hook';
const { constants: { MINIAPP, WECHAT_MINIPROGRAM, BYTEDANCE_MICROAPP, BAIDU_SMARTPROGRAM, KUAISHOU_MINIPROGRAM } } = miniappBuilderShared;
const miniappPlatforms = [MINIAPP, WECHAT_MINIPROGRAM, BYTEDANCE_MICROAPP, BAIDU_SMARTPROGRAM, KUAISHOU_MINIPROGRAM];
interface IRaxAppUserConfig extends IUserConfig {
targets: string[];
store?: boolean;
web?: any;
experiments?: {
minifyCSSModules?: boolean;
};
webpack5?: boolean;
router?: boolean;
}
const getBuiltInPlugins: IGetBuiltInPlugins = (userConfig: IRaxAppUserConfig) => {
const { targets = ['web'], store = true, router = true, webpack5, experiments = {} } = userConfig;
const coreOptions: Json = {
framework: 'rax',
alias: 'rax-app',
};
init(webpack5);
hijackWebpack(webpack5);
// built-in plugins for rax app
const builtInPlugins: IPluginList = [
['build-plugin-app-core', coreOptions],
'build-plugin-rax-app',
'build-plugin-ice-config',
];
if (store) {
builtInPlugins.push('build-plugin-rax-store');
}
if (targets.includes('web')) {
builtInPlugins.push('build-plugin-rax-web');
}
if (targets.includes('weex')) {
builtInPlugins.push('build-plugin-rax-weex');
}
if (targets.includes('kraken')) {
builtInPlugins.push('build-plugin-rax-kraken');
}
const isMiniAppTargeted = targets.some((target) => miniappPlatforms.includes(target));
if (isMiniAppTargeted) {
builtInPlugins.push('build-plugin-rax-miniapp');
}
if (userConfig.web) {
if (userConfig.web.pha) {
builtInPlugins.push('build-plugin-rax-pha');
}
// Make ssr plugin after base plugin which need registerTask, the action will override the devServer config
if (userConfig.web.ssr) {
builtInPlugins.push('build-plugin-ssr');
}
}
if (router) {
builtInPlugins.push('build-plugin-rax-router');
}
builtInPlugins.push('build-plugin-ice-logger');
if (experiments.minifyCSSModules === true) {
builtInPlugins.push('build-plugin-minify-classname');
}
return builtInPlugins;
};
export = getBuiltInPlugins;
//回到ice-scripts
#!/usr/bin/env node
const program = require('commander');
const checkNodeVersion = require('./checkNodeVersion');
const start = require('./start');
const build = require('./build');
const test = require('./test');
module.exports = async (getBuiltInPlugins, forkChildProcessPath, packageInfo, extendCli) => {
if (packageInfo.ICEJS_INFO) {
console.log(
${packageInfo.name} ${packageInfo.version}
,(${packageInfo.__ICEJS_INFO__.name} ${packageInfo.__ICEJS_INFO__.version})
);
} else {
console.log(packageInfo.name, packageInfo.version);
}
// finish check before run command
checkNodeVersion(packageInfo.engines.node, packageInfo.name);
program
.version(packageInfo.version)
.usage(' [options]');
program
.command('build')
.description('build project')
.allowUnknownOption()
.option('--config ', 'use custom config')
.option('--rootDir ', 'project root directory')
.action(async function() {
await build(getBuiltInPlugins);
});
program
.command('start')
.description('start server')
.allowUnknownOption()
.option('--config ', 'use custom config')
.option('-h, --host ', 'dev server host', '0.0.0.0')
.option('-p, --port ', 'dev server port')
.option('--rootDir ', 'project root directory')
.action(async function() {
await start(getBuiltInPlugins, forkChildProcessPath);
});
program
.command('test')
.description('run tests with jest')
.allowUnknownOption() // allow jest config
.option('--config ', 'use custom config')
.action(async function() {
await test(getBuiltInPlugins);
});
//rax这边过来是没有的
if (typeof extendCli === 'function') {
extendCli(program);
}
program.parse(process.argv);
const proc = program.runningCommand;
if (proc) {
proc.on('close', process.exit.bind(process));
proc.on('error', () => {
process.exit(1);
});
}
const subCmd = program.args[0];
if (!subCmd) {
program.help();
}
};
//主要是处理监听文件、restart的时候inspector的端口confilct,然后重点就是fork forkChildProcessPath程序文件进程。
#!/usr/bin/env node
const { fork } = require('child_process');
const parse = require('yargs-parser');
const chokidar = require('chokidar');
const detect = require('detect-port');
const path = require('path');
const log = require('build-scripts/lib/utils/log');
let child = null;
const rawArgv = parse(process.argv.slice(2));
const configPath = path.resolve(rawArgv.config || 'build.json');
const inspectRegExp = /^--(inspect(?:-brk)?)(?:=(?:([^:]+):)?(\d+))?$/;
async function modifyInspectArgv(execArgv, processArgv) {
/**
*/
const result = await Promise.all(
execArgv.map(async item => {
const matchResult = inspectRegExp.exec(item);
if (!matchResult) {
return item;
}
// eslint-disable-next-line
const [_, command, ip, port = 9229] = matchResult;
const nPort = +port;
const newPort = await detect(nPort);
return
--${command}=${ip ?
${ip}:: ''}${newPort}
;})
);
/**
*/
if (processArgv.inspect) {
const matchResult = /(?:([^:]+):)?(\d+)/.exec(rawArgv.inspect);
// eslint-disable-next-line
const [_, ip, port = 9229] = matchResult || [];
const newPort = await detect(port);
result.push(
--inspect-brk=${ip ?
${ip}:: ''}${newPort}
);}
return result;
}
function restartProcess(forkChildProcessPath) {
(async () => {
// remove the inspect related argv when passing to child process to avoid port-in-use error
const argv = await modifyInspectArgv(process.execArgv, rawArgv);
const nProcessArgv = process.argv.slice(2).filter((arg) => arg.indexOf('--inspect') === -1);
child = fork(forkChildProcessPath, nProcessArgv, { execArgv: argv });
child.on('message', data => {
if (data && data.type === 'RESTART_DEV') {
child.kill();
restartProcess(forkChildProcessPath);
}
if (process.send) {
process.send(data);
}
});
})();
}
module.exports = (getBuiltInPlugins, forkChildProcessPath) => {
restartProcess(forkChildProcessPath);
const watcher = chokidar.watch(configPath, {
ignoreInitial: true,
});
watcher.on('change', function() {
console.log('\n');
log.info('build.json has been changed');
log.info('restart dev server');
// add process env for mark restart dev process
process.env.RESTART_DEV = true;
child.kill();
restartProcess(forkChildProcessPath);
});
watcher.on('error', error => {
log.error('fail to watch file', error);
process.exit(1);
});
};
fork启动的代码:
#!/usr/bin/env node
const { childProcessStart } = require('create-cli-utils');
const getBuiltInPlugins = require('../lib');
(async () => {
await childProcessStart(getBuiltInPlugins);
})();
#!/usr/bin/env node
const detect = require('detect-port');
const inquirer = require('inquirer');
const parse = require('yargs-parser');
const log = require('build-scripts/lib/utils/log');
const { isAbsolute, join } = require('path');
const BuildService = require('./buildService');
const rawArgv = parse(process.argv.slice(2), {
configuration: { 'strip-dashed': true }
});
const DEFAULT_PORT = rawArgv.port || process.env.PORT || 3333;
const defaultPort = parseInt(DEFAULT_PORT, 10);
module.exports = async (getBuiltInPlugins) => {
let newPort = await detect(defaultPort);
if (newPort !== defaultPort) {
const question = {
type: 'confirm',
name: 'shouldChangePort',
message:
${defaultPort} 端口已被占用,是否使用 ${newPort} 端口启动?
,default: true
};
const answer = await inquirer.prompt(question);
if (!answer.shouldChangePort) {
newPort = null;
}
}
if (newPort === null) {
process.exit(1);
}
process.env.NODE_ENV = 'development';
rawArgv.port = parseInt(newPort, 10);
const { rootDir = process.cwd() } = rawArgv;
delete rawArgv.rootDir;
// ignore _ in rawArgv
delete rawArgv._;
try {
const service = new BuildService({
command: 'start',
args: { ...rawArgv },
getBuiltInPlugins,
rootDir: isAbsolute(rootDir) ? rootDir : join(process.cwd(), rootDir),
});
const devServer = await service.run({});
} catch (err) {
log.error(err.message);
console.error(err);
process.exit(1);
}
};
//走service 和 context,主要是context
constructor(options: IContextOptions) {
const {
command,
rootDir = process.cwd(),
args = {},
} = options || {};
}
new BuildService 实例结束后,去执行await service.run({});
public run = async <T, P>(options?: T): Promise
=> {
const { command, commandArgs } = this;
log.verbose(
'OPTIONS',
${command} cliOptions: ${JSON.stringify(commandArgs, null, 2)}
,);
try {
await this.setUp();
} catch (err) {
log.error('CONFIG', chalk.red('Failed to get config.'));
await this.applyHook(
error
, { err });throw err;
}
const commandModule = this.getCommandModule({ command, commandArgs, userConfig: this.userConfig });
return commandModule(this, options);
}
public setUp = async (): Promise<ITaskConfig[]> => {
await this.resolveConfig();
await this.runPlugins();
await this.runConfigModification();
await this.runUserConfig();
await this.runWebpackFunctions();
await this.runCliOption();
// filter webpack config by cancelTaskNames
this.configArr = this.configArr.filter(
config => !this.cancelTaskNames.includes(config.name),
);
return this.configArr;
};
//关键的是走runPlugins,其实就是遍历执行build.json里注册的,以及getBuiltInPlugins带过来的。
我们以plugin-rax-kraken为例:
module.exports = (api) => {
const { getValue, context, registerTask, onGetWebpackConfig, applyMethod } = api;
const { userConfig = {}, webpack } = context;
const { RawSource } = webpack.sources || webpackSources;
const getWebpackBase = getValue(GET_RAX_APP_WEBPACK_CONFIG);
const tempDir = getValue('TEMP_PATH');
const chainConfig = getWebpackBase(api, {
target,
babelConfigOptions: { styleSheet: userConfig.inlineStyle },
progressOptions: {
name: 'Kraken',
},
});
chainConfig.name(target);
chainConfig.taskName = target;
setEntry(chainConfig, context);
registerTask(target, chainConfig);
onGetWebpackConfig(target, (config) => {
const { command } = context;
const krakenConfig = userConfig.kraken || {};
const staticConfig = getValue('staticConfig');
});
onGetWebpackConfig(target, (config) => {
config
.plugin('BuildKBCPlugin')
.use(class BuildKBCPlugin {
apply(compiler) {
const qjsc = new Qjsc();
processAssets({
pluginName: 'BuildKBCPlugin',
compiler,
}, ({ compilation, assets, callback }) => {
const injected = applyMethod('rax.getInjectedHTML');
});
};
这里比较关键的两个方法:registerTask和onGetWebpackConfig。他们就是为每种构建任务,修改webpack配置。
// 通过registerTask注册,存放初始的webpack-chain配置
private configArr: ITaskConfig[];
public registerTask: IRegisterTask = (name, chainConfig) => {
const exist = this.configArr.find((v): boolean => v.name === name);
if (!exist) {
this.configArr.push({
name,
chainConfig,
modifyFunctions: [],
});
} else {
throw new Error(
[Error] config '${name}' already exists!
);}
};
public onGetWebpackConfig: IOnGetWebpackConfig = (
...args: IOnGetWebpackConfigArgs
) => {
this.modifyConfigFns.push(args);
};
最后,我们这边的就是start模块。
const commandModule = this.getCommandModule({ command, commandArgs, userConfig: this.userConfig });
return commandModule(this, options);
export = async function(context: Context, options?: IRunOptions): Promise<void | ITaskConfig[] | WebpackDevServer> {
const { eject } = options || {};
const configArr = context.getWebpackConfig();
const { command, commandArgs, webpack, applyHook } = context;
await applyHook(
before.${command}.load
, { args: commandArgs, webpackConfig: configArr });// eject config
if (eject) {
return configArr;
}
if (!configArr.length) {
const errorMsg = 'No webpack config found.';
log.warn('CONFIG', errorMsg);
await applyHook(
error
, { err: new Error(errorMsg) });return;
}
let serverUrl = '';
let devServerConfig: DevServerConfig = {
port: commandArgs.port || 3333,
host: commandArgs.host || '0.0.0.0',
https: commandArgs.https || false,
};
for (const item of configArr) {
const { chainConfig } = item;
const config = chainConfig.toConfig() as WebpackOptionsNormalized;
if (config.devServer) {
devServerConfig = deepmerge(devServerConfig, config.devServer);
}
// if --port or process.env.PORT has been set, overwrite option port
if (process.env.USE_CLI_PORT) {
devServerConfig.port = commandArgs.port;
}
}
const webpackConfig = configArr.map(v => v.chainConfig.toConfig());
await applyHook(
before.${command}.run
, {args: commandArgs,
config: webpackConfig,
});
let compiler;
try {
compiler = webpack(webpackConfig);
} catch (err) {
log.error('CONFIG', chalk.red('Failed to load webpack config.'));
await applyHook(
error
, { err });throw err;
}
const protocol = devServerConfig.https ? 'https' : 'http';
const urls = prepareURLs(
protocol,
devServerConfig.host,
devServerConfig.port,
);
serverUrl = urls.localUrlForBrowser;
let isFirstCompile = true;
// typeof(stats) is webpack.compilation.MultiStats
compiler.hooks.done.tap('compileHook', async stats => {
const isSuccessful = webpackStats({
urls,
stats,
isFirstCompile,
});
if (isSuccessful) {
isFirstCompile = false;
}
await applyHook(
after.${command}.compile
, {url: serverUrl,
urls,
isFirstCompile,
stats,
});
});
let devServer: WebpackDevServer;
// require webpack-dev-server after context setup
// context may hijack webpack resolve
// eslint-disable-next-line @typescript-eslint/no-var-requires
const DevServer = require('webpack-dev-server');
// static method getFreePort in v4
if (DevServer.getFreePort) {
devServer = new DevServer(devServerConfig, compiler);
} else {
devServer = new DevServer(compiler, devServerConfig);
}
await applyHook(
before.${command}.devServer
, {url: serverUrl,
urls,
devServer,
});
if (devServer.startCallback) {
devServer.startCallback(
() => {
applyHook(
after.${command}.devServer
, {url: serverUrl,
urls,
devServer,
});
},
);
} else {
devServer.listen(devServerConfig.port, devServerConfig.host, async (err: Error) => {
if (err) {
log.info('WEBPACK',chalk.red('[ERR]: Failed to start webpack dev server'));
log.error('WEBPACK', (err.stack || err.toString()));
}
await applyHook(
after.${command}.devServer
, {url: serverUrl,
urls,
devServer,
err,
});
});
}
return devServer;
};
这里真正生成WDS的地方。
The text was updated successfully, but these errors were encountered: