Child_precess exec is broken?
The accidents are as follows:
A bit of background: During development of the egg.js project (note the following), at the service layer, you need to open child processes to perform script operations that trigger the document processing and build of Vuepress.
The project catalog is as follows:
The abbreviated code is as follows:
// app/service/doc.js const Service = require('egg').Service const buildDir = p => path.join(__dirname, '.. /.. /vuepress', p) const buildFilePath = buildDir('/build.js') const path = require('path') const { promisify } = require('util') const Exec = promisify(require('child_process').exec) class DocService extends Service {async get() { Const {stdout, stderr} = await triggerBuild() // omitted some code... }} function triggerBuild() {const shell = 'node ${buildFilePath}' return exec(shell, {// the child's working directory CWD: buildDir('') }) } module.exports = DocServiceCopy the code
// vuepress/build.js const { promisify } = require('util') const exec = promisify(require('child_process').exec) async Start () {// omit some code... Const {stdout, stderr} = awiat handleBuildDoc() // omit some code... } function handleBuildDoc() {// omit some code... const shell = 'npm run build' return exec(shell, { cwd: __dirname, }) }Copy the code
“Of course, there may be other questions as to why there are two processes performing this process, one is perfectly sufficient, because they need to do different things, they are separated to ensure that the open closed principle, intermediate products. Temp is the bridge.”
Back to the point, does the above code look good at first glance?
However, a Warn is thrown at run time
❝
npm WARN lifecycle The node binary used for scripts is /var/folders/ J5 / LzPHF_YN4154MS5pn9C_9qh40000GN /T/ YARN –1635578680391-0.7100593669876971/node but NPM is using /usr/local/bin/node itself. Use the –scripts-prepend-node-path option to include the path for the node binary npm was executed with.
❞
NPM run-script is running in an abnormal path, so you need to add an option –scripts-prepend-node-path. This is a warning caused by an abnormal path, and does not affect the execution result.
“See NPM run-script[1] for details.”
Change the above shell to the following and warn will not appear.
const shell = 'npm run build --scripts-prepend-node-path=auto'
Copy the code
In fact, if it is only WARN, it may be directly ignored, after all, it does not affect the normal process. “But there is a big Error waiting behind!!”
❝
Cannot find module ‘/Users/zhi/Desktop/zhuanzhuan/node_webview_backend/vuepress/docs/.vuepress/dist/manifest/client.json
❞
To tell the truth this stuff just saw, immediately checked vuepress/docs /. Vuepress/dist/manifest, meng force, How only vuepress/docs /. Vuepress/dist/manifest/server json. “I’m running NPM run build, but I’m not producing build intermediates.”
The air was suddenly quiet. I, too, was lost in thought.
And then looking at the error stack information,
'- /Users/zhi/Desktop/zhuanzhuan/node_webview_backend/vuepress/node_modules/@vuepress/core/lib/node/build/index.js\n' +\n" + " '- /Users/zhi/Desktop/zhuanzhuan/node_webview_backend/vuepress/node_modules/@vuepress/core/lib/node/App.js\n' +\n" + " '- /Users/zhi/Desktop/zhuanzhuan/node_webview_backend/vuepress/node_modules/@vuepress/core/lib/index.js\n' +\n" + " '- /Users/zhi/Desktop/zhuanzhuan/node_webview_backend/vuepress/node_modules/vuepress/lib/registerCoreCommands.js\n' +\n" + " '- /Users/zhi/Desktop/zhuanzhuan/node_webview_backend/vuepress/node_modules/vuepress/cli.js\n' +\n" +Copy the code
In node_modules / @ vuepress/core/lib/node/build/index, js saw render function will use the manifest/client. The json,
// node_modules/@vuepress/core/lib/node/build/index.js
/**
* Compile and render pages.
*
* @returns {Promise<void>}
* @api public
*/
async render () {
// compile!
const stats = await compile([this.clientConfig, this.serverConfig])
const serverBundle = require(path.resolve(this.outDir, 'manifest/server.json'))
const clientManifest = require(path.resolve(this.outDir, 'manifest/client.json'))
// remove manifests after loading them.
await fs.remove(path.resolve(this.outDir, 'manifest'))
// ...
}
// ...
/**
* Compile a webpack application and return stats json.
*
* @param {Object} config
* @returns {Promise<Object>}
*/
function compile (config) {
return new Promise((resolve, reject) => {
webpack(config, (err, stats) => {
if (err) {
return reject(err)
}
if (stats.hasErrors()) {
stats.toJson().errors.forEach(err => {
console.error(err)
})
reject(new Error(`Failed to compile with errors.`))
return
}
if (env.isDebug && stats.hasWarnings()) {
stats.toJson().warnings.forEach(warning => {
console.warn(warning)
})
}
resolve(stats.toJson({ modules: false }))
})
})
}
Copy the code
After looking at the source code of Vuepress, the preliminary judgment should be that there is an exception when compile, and the target file product is not produced. Node_modules /@vuepress search client. Json
As you can see, the node_modules / @ vuepress/core/lib/node/webpack/createClientConfig js file is also used in this file, this should is the place where the output of the file export, specific content is as follows:
module.exports = function createClientConfig (ctx) { const { env } = require('@vuepress/shared-utils') const createBaseConfig = require('./createBaseConfig') const safeParser = require('postcss-safe-parser') const config = createBaseConfig(ctx) //... // generate client manifest only during build if (process.env.NODE_ENV === 'production') { // This is a temp build of Vue -server-renderer/ client-plugin.console. log(' Debug info: ', '---- output manifest/client.json ----', config) config .plugin('ssr-client') .use(require('./ClientPlugin'), [{ filename: 'manifest/client.json' }]) } //... }Copy the code
So I added log information inside the function, re-executed it, and found that it had not been executed at all, and then printed process.env.node_env, which was ‘development’. NPM run build NODE_ENV is development. .
My child_precess exec has a problem.
Obviously not, because it is normal for egg.js to run in dev mode and the eggjs main process environment variable env.node_env to be in dev mode.
Process. Env. NODE_ENV = “development” before exec(‘ NPM run build’) Then process.env.node_env = old_NODE_ENV after the callback ends. That’s the perfect solution. Practice proved that it did, and it finally worked.
In fact, the above forcible modification of the global variables of the main process is obviously a side effect and may one day create other unknown problems, creating holes for others and for yourself. Take a look at the official document nodejs.cn/api-v14/chi… Env
exec(shell, { cwd: __dirname, env: { ... process.env, NODE_ENV: 'production', }, })Copy the code
conclusion
This is the share of the pit experience.
Here are some of my problem-solving experiences and ideas when encountering problems:
- Don’t panic, calm down to think 🤔
- Google can solve 80%+ of your problems
- Do not have ideas or answers, according to the error stack information, to trace the source code (do not have psychological stress and fear of the source code)
- Regular debug to troubleshoot problems
- When you find the answer, don’t think you’re done. Think about whether it’s the best solution and whether it’s going to bury someone else
- Review summary, how can we solve the problem faster and better next time 🎉🎉🎉
Reference
[1] NPM run – script: www.axihe.com/api/npm/cli…
[2] child_process: exec: nodejs. Cn/API – v14 / chi…