Node can't find certain modules after synchronous install





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







1















I've got a script that synchronously installs non-built-in modules at startup that looks like this



const cp = require('child_process')

function requireOrInstall (module) {
try {
require.resolve(module)
} catch (e) {
console.log(`Could not resolve "${module}"nInstalling`)
cp.execSync(`npm install ${module}`)
console.log(`"${module}" has been installed`)
}
console.log(`Requiring "${module}"`)
try {
return require(module)
} catch (e) {
console.log(require.cache)
console.log(e)
}
}

const http = require('http')
const path = require('path')
const fs = require('fs')
const ffp = requireOrInstall('find-free-port')
const express = requireOrInstall('express')
const socket = requireOrInstall('socket.io')
// List goes on...


When I uninstall modules, they get installed successfully when I start the server again, which is what I want. However, the script starts throwing Cannot find module errors when I uninstall the first or first two modules of the list that use the function requireOrInstall. That's right, the errors only occur when the script has to install either the first or the first two modules, not when only the second module needs installing.



In this example, the error will be thrown when I uninstall find-free-port, unless I move its require at least one spot down ¯_(• _ •)_/¯



I've also tried adding a delay directly after the synchronous install to give it a little more breathing time with the following two lines:



var until = new Date().getTime() + 1000
while (new Date().getTime() < until) {}


The pause was there. It didn't fix anything.



@velocityzen came with the idea to check the cache, which I've now added to the script. It doesn't show anything out of the ordinary.



@vaughan's comment on another question noted that this exact error occurs when requiring a module twice. I've changed the script to use require.resolve(), but the error still remains.



Does anybody know what could be causing this?



Edit



Since the question has been answered, I'm posting the one-liner (139 characters!). It doesn't globally define child_modules, has no last try-catch and doesn't log anything in the console:



const req=async m=>{let r=require;try{r.resolve(m)}catch(e){r('child_process').execSync('npm i '+m);await setImmediate(()=>{})}return r(m)}


The name of the function is req() and can be used like in @alex-rokabilis' answer.










share|improve this question




















  • 1





    I sort of wonder what problem you're actually trying to solve with this.

    – robertklep
    Nov 12 '18 at 21:50











  • I simply want the modules to be installed and found in one go, without errors. That's all.

    – Gust van de Wal
    Nov 12 '18 at 22:01






  • 3





    That's what package.json and npm install are for, though.

    – robertklep
    Nov 12 '18 at 22:02











  • I know. Only having to add const package = require('package') because of a line of 156 characters (minified) at the top of the script is a nice perk, though.

    – Gust van de Wal
    Nov 12 '18 at 22:11






  • 2





    you can't solve this without messing with node's internals, not an easy problem

    – mihai
    Nov 16 '18 at 13:36


















1















I've got a script that synchronously installs non-built-in modules at startup that looks like this



const cp = require('child_process')

function requireOrInstall (module) {
try {
require.resolve(module)
} catch (e) {
console.log(`Could not resolve "${module}"nInstalling`)
cp.execSync(`npm install ${module}`)
console.log(`"${module}" has been installed`)
}
console.log(`Requiring "${module}"`)
try {
return require(module)
} catch (e) {
console.log(require.cache)
console.log(e)
}
}

const http = require('http')
const path = require('path')
const fs = require('fs')
const ffp = requireOrInstall('find-free-port')
const express = requireOrInstall('express')
const socket = requireOrInstall('socket.io')
// List goes on...


When I uninstall modules, they get installed successfully when I start the server again, which is what I want. However, the script starts throwing Cannot find module errors when I uninstall the first or first two modules of the list that use the function requireOrInstall. That's right, the errors only occur when the script has to install either the first or the first two modules, not when only the second module needs installing.



In this example, the error will be thrown when I uninstall find-free-port, unless I move its require at least one spot down ¯_(• _ •)_/¯



I've also tried adding a delay directly after the synchronous install to give it a little more breathing time with the following two lines:



var until = new Date().getTime() + 1000
while (new Date().getTime() < until) {}


The pause was there. It didn't fix anything.



@velocityzen came with the idea to check the cache, which I've now added to the script. It doesn't show anything out of the ordinary.



@vaughan's comment on another question noted that this exact error occurs when requiring a module twice. I've changed the script to use require.resolve(), but the error still remains.



Does anybody know what could be causing this?



Edit



Since the question has been answered, I'm posting the one-liner (139 characters!). It doesn't globally define child_modules, has no last try-catch and doesn't log anything in the console:



const req=async m=>{let r=require;try{r.resolve(m)}catch(e){r('child_process').execSync('npm i '+m);await setImmediate(()=>{})}return r(m)}


The name of the function is req() and can be used like in @alex-rokabilis' answer.










share|improve this question




















  • 1





    I sort of wonder what problem you're actually trying to solve with this.

    – robertklep
    Nov 12 '18 at 21:50











  • I simply want the modules to be installed and found in one go, without errors. That's all.

    – Gust van de Wal
    Nov 12 '18 at 22:01






  • 3





    That's what package.json and npm install are for, though.

    – robertklep
    Nov 12 '18 at 22:02











  • I know. Only having to add const package = require('package') because of a line of 156 characters (minified) at the top of the script is a nice perk, though.

    – Gust van de Wal
    Nov 12 '18 at 22:11






  • 2





    you can't solve this without messing with node's internals, not an easy problem

    – mihai
    Nov 16 '18 at 13:36














1












1








1


1






I've got a script that synchronously installs non-built-in modules at startup that looks like this



const cp = require('child_process')

function requireOrInstall (module) {
try {
require.resolve(module)
} catch (e) {
console.log(`Could not resolve "${module}"nInstalling`)
cp.execSync(`npm install ${module}`)
console.log(`"${module}" has been installed`)
}
console.log(`Requiring "${module}"`)
try {
return require(module)
} catch (e) {
console.log(require.cache)
console.log(e)
}
}

const http = require('http')
const path = require('path')
const fs = require('fs')
const ffp = requireOrInstall('find-free-port')
const express = requireOrInstall('express')
const socket = requireOrInstall('socket.io')
// List goes on...


When I uninstall modules, they get installed successfully when I start the server again, which is what I want. However, the script starts throwing Cannot find module errors when I uninstall the first or first two modules of the list that use the function requireOrInstall. That's right, the errors only occur when the script has to install either the first or the first two modules, not when only the second module needs installing.



In this example, the error will be thrown when I uninstall find-free-port, unless I move its require at least one spot down ¯_(• _ •)_/¯



I've also tried adding a delay directly after the synchronous install to give it a little more breathing time with the following two lines:



var until = new Date().getTime() + 1000
while (new Date().getTime() < until) {}


The pause was there. It didn't fix anything.



@velocityzen came with the idea to check the cache, which I've now added to the script. It doesn't show anything out of the ordinary.



@vaughan's comment on another question noted that this exact error occurs when requiring a module twice. I've changed the script to use require.resolve(), but the error still remains.



Does anybody know what could be causing this?



Edit



Since the question has been answered, I'm posting the one-liner (139 characters!). It doesn't globally define child_modules, has no last try-catch and doesn't log anything in the console:



const req=async m=>{let r=require;try{r.resolve(m)}catch(e){r('child_process').execSync('npm i '+m);await setImmediate(()=>{})}return r(m)}


The name of the function is req() and can be used like in @alex-rokabilis' answer.










share|improve this question
















I've got a script that synchronously installs non-built-in modules at startup that looks like this



const cp = require('child_process')

function requireOrInstall (module) {
try {
require.resolve(module)
} catch (e) {
console.log(`Could not resolve "${module}"nInstalling`)
cp.execSync(`npm install ${module}`)
console.log(`"${module}" has been installed`)
}
console.log(`Requiring "${module}"`)
try {
return require(module)
} catch (e) {
console.log(require.cache)
console.log(e)
}
}

const http = require('http')
const path = require('path')
const fs = require('fs')
const ffp = requireOrInstall('find-free-port')
const express = requireOrInstall('express')
const socket = requireOrInstall('socket.io')
// List goes on...


When I uninstall modules, they get installed successfully when I start the server again, which is what I want. However, the script starts throwing Cannot find module errors when I uninstall the first or first two modules of the list that use the function requireOrInstall. That's right, the errors only occur when the script has to install either the first or the first two modules, not when only the second module needs installing.



In this example, the error will be thrown when I uninstall find-free-port, unless I move its require at least one spot down ¯_(• _ •)_/¯



I've also tried adding a delay directly after the synchronous install to give it a little more breathing time with the following two lines:



var until = new Date().getTime() + 1000
while (new Date().getTime() < until) {}


The pause was there. It didn't fix anything.



@velocityzen came with the idea to check the cache, which I've now added to the script. It doesn't show anything out of the ordinary.



@vaughan's comment on another question noted that this exact error occurs when requiring a module twice. I've changed the script to use require.resolve(), but the error still remains.



Does anybody know what could be causing this?



Edit



Since the question has been answered, I'm posting the one-liner (139 characters!). It doesn't globally define child_modules, has no last try-catch and doesn't log anything in the console:



const req=async m=>{let r=require;try{r.resolve(m)}catch(e){r('child_process').execSync('npm i '+m);await setImmediate(()=>{})}return r(m)}


The name of the function is req() and can be used like in @alex-rokabilis' answer.







javascript node.js asynchronous npm npm-install






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 20 '18 at 9:13







Gust van de Wal

















asked Nov 12 '18 at 21:03









Gust van de WalGust van de Wal

3,3081338




3,3081338








  • 1





    I sort of wonder what problem you're actually trying to solve with this.

    – robertklep
    Nov 12 '18 at 21:50











  • I simply want the modules to be installed and found in one go, without errors. That's all.

    – Gust van de Wal
    Nov 12 '18 at 22:01






  • 3





    That's what package.json and npm install are for, though.

    – robertklep
    Nov 12 '18 at 22:02











  • I know. Only having to add const package = require('package') because of a line of 156 characters (minified) at the top of the script is a nice perk, though.

    – Gust van de Wal
    Nov 12 '18 at 22:11






  • 2





    you can't solve this without messing with node's internals, not an easy problem

    – mihai
    Nov 16 '18 at 13:36














  • 1





    I sort of wonder what problem you're actually trying to solve with this.

    – robertklep
    Nov 12 '18 at 21:50











  • I simply want the modules to be installed and found in one go, without errors. That's all.

    – Gust van de Wal
    Nov 12 '18 at 22:01






  • 3





    That's what package.json and npm install are for, though.

    – robertklep
    Nov 12 '18 at 22:02











  • I know. Only having to add const package = require('package') because of a line of 156 characters (minified) at the top of the script is a nice perk, though.

    – Gust van de Wal
    Nov 12 '18 at 22:11






  • 2





    you can't solve this without messing with node's internals, not an easy problem

    – mihai
    Nov 16 '18 at 13:36








1




1





I sort of wonder what problem you're actually trying to solve with this.

– robertklep
Nov 12 '18 at 21:50





I sort of wonder what problem you're actually trying to solve with this.

– robertklep
Nov 12 '18 at 21:50













I simply want the modules to be installed and found in one go, without errors. That's all.

– Gust van de Wal
Nov 12 '18 at 22:01





I simply want the modules to be installed and found in one go, without errors. That's all.

– Gust van de Wal
Nov 12 '18 at 22:01




3




3





That's what package.json and npm install are for, though.

– robertklep
Nov 12 '18 at 22:02





That's what package.json and npm install are for, though.

– robertklep
Nov 12 '18 at 22:02













I know. Only having to add const package = require('package') because of a line of 156 characters (minified) at the top of the script is a nice perk, though.

– Gust van de Wal
Nov 12 '18 at 22:11





I know. Only having to add const package = require('package') because of a line of 156 characters (minified) at the top of the script is a nice perk, though.

– Gust van de Wal
Nov 12 '18 at 22:11




2




2





you can't solve this without messing with node's internals, not an easy problem

– mihai
Nov 16 '18 at 13:36





you can't solve this without messing with node's internals, not an easy problem

– mihai
Nov 16 '18 at 13:36












3 Answers
3






active

oldest

votes


















1





+150









It seems that the require operation after an npm install needs a certain delay.
Also the problem is worse in windows, it will always fail if the module needs to be npm installed.
It's like at a specific event snapshot is already known what modules can be required and what cannot. Probably that's why require.cache was mentioned in the comments. Nevertheless I suggest you to check the 2 following solutions.



1) Use a delay



const cp = require("child_process");

const requireOrInstall = async module => {
try {
require.resolve(module);
} catch (e) {
console.log(`Could not resolve "${module}"nInstalling`);
cp.execSync(`npm install ${module}`);
// Use one of the two awaits below
// The first one waits 1000 milliseconds
// The other waits until the next event cycle
// Both work
await new Promise(resolve => setTimeout(() => resolve(), 1000));
await new Promise(resolve => setImmediate(() => resolve()));
console.log(`"${module}" has been installed`);
}
console.log(`Requiring "${module}"`);
try {
return require(module);
} catch (e) {
console.log(require.cache);
console.log(e);
}
}

const main = async() => {
const http = require("http");
const path = require("path");
const fs = require("fs");
const ffp = await requireOrInstall("find-free-port");
const express = await requireOrInstall("express");
const socket = await requireOrInstall("socket.io");
}

main();


await always needs a promise to work with, but it's not needed to explicitly create one as await will wrap whatever it is waiting for in a promise if it isn't handed one.



2) Use a cluster



const cp = require("child_process");

function requireOrInstall(module) {
try {
require.resolve(module);
} catch (e) {
console.log(`Could not resolve "${module}"nInstalling`);
cp.execSync(`npm install ${module}`);
console.log(`"${module}" has been installed`);
}
console.log(`Requiring "${module}"`);
try {
return require(module);
} catch (e) {
console.log(require.cache);
console.log(e);
process.exit(1007);
}
}

const cluster = require("cluster");

if (cluster.isMaster) {
cluster.fork();
cluster.on("exit", (worker, code, signal) => {
if (code === 1007) {
cluster.fork();
}
});
} else if (cluster.isWorker) {
// The real work here for the worker

const http = require("http");
const path = require("path");
const fs = require("fs");
const ffp = requireOrInstall("find-free-port");
const express = requireOrInstall("express");
const socket = requireOrInstall("socket.io");

process.exit(0);
}


The idea here is to re-run the process in case of a missing module. This way we fully reproduce a manual npm install so as you guess it works! Also it seems more synchronous rather the first option, but a bit more complex.






share|improve this answer


























  • I used your first solution. It's elegant ES6 code and it did the trick with only a couple of extra lines and without the use of third party packages (which was kind of the whole point for me). Thanks!

    – Gust van de Wal
    Nov 19 '18 at 20:52











  • I am glad that you used/liked my answer! My only concern is about await setTimeout(() => {}, 1000); Are you sure it can work like this? Without a promise? I cannot reproduce the delay that way, in chrome at least.

    – Alex Michailidis
    Nov 20 '18 at 7:00






  • 1





    I seem to have blindly pasted the setTimeout option in there after it worked in a test. I hadn't noticed it didn't actually wait 1 second. Good catch!

    – Gust van de Wal
    Nov 20 '18 at 9:07



















1














I think your best option is either:




  • (ugly) to install package globally, instead of locally

  • (best solution ?) to define YOUR new 'package repository installation', when installing, AND when requiring


First, you may consider using the npm-programmatic package.



Then, you may define your repository path with something like:



const PATH='/tmp/myNodeModuleRepository';


Then, replace your installation instruction with something like:



const npm = require('npm-programmatic');
npm.install(`${module}`, {
cwd: PATH,
save:true
}


Eventually, replace your failback require instruction, with something like:



return require(module, { paths: [ PATH ] });


If it is still not working, you may update the require.cache variable, for instance to invalide a module, you can do something like:



delete require.cache[process.cwd() + 'node_modules/bluebird/js/release/bluebird.js'];


You may need to update it manually, to add information about your new module, before loading it.






share|improve this answer































    0














    cp.execSync is an async call so try check if the module is installed in it's call back function. I have tried it, installation is clean now:






    const cp = require('child_process')

    function requireOrInstall (module) {
    try {
    require.resolve(module)
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`)
    cp.execSync(`npm install ${module}`, () => {
    console.log(`"${module}" has been installed`)
    try {
    return require(module)
    } catch (e) {
    console.log(require.cache)
    console.log(e)
    }
    })

    }
    console.log(`Requiring "${module}"`)

    }

    const http = require('http')
    const path = require('path')
    const fs = require('fs')
    const ffp = requireOrInstall('find-free-port')
    const express = requireOrInstall('express')
    const socket = requireOrInstall('socket.io')






    When node_modules not available yet :
    enter image description here



    When node_modules available already:
    enter image description here







    share|improve this answer


























    • .exec() is asynchronous, not .execSync(). it therefore doesn't have a callback function. Returning the required module also won't work here, because you're returning the require from inside the callback, not requireOrInstall(). It's not the installation that fails, but the modules are unfindable.

      – Gust van de Wal
      Nov 19 '18 at 20:18














    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270058%2fnode-cant-find-certain-modules-after-synchronous-install%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1





    +150









    It seems that the require operation after an npm install needs a certain delay.
    Also the problem is worse in windows, it will always fail if the module needs to be npm installed.
    It's like at a specific event snapshot is already known what modules can be required and what cannot. Probably that's why require.cache was mentioned in the comments. Nevertheless I suggest you to check the 2 following solutions.



    1) Use a delay



    const cp = require("child_process");

    const requireOrInstall = async module => {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    // Use one of the two awaits below
    // The first one waits 1000 milliseconds
    // The other waits until the next event cycle
    // Both work
    await new Promise(resolve => setTimeout(() => resolve(), 1000));
    await new Promise(resolve => setImmediate(() => resolve()));
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    }
    }

    const main = async() => {
    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = await requireOrInstall("find-free-port");
    const express = await requireOrInstall("express");
    const socket = await requireOrInstall("socket.io");
    }

    main();


    await always needs a promise to work with, but it's not needed to explicitly create one as await will wrap whatever it is waiting for in a promise if it isn't handed one.



    2) Use a cluster



    const cp = require("child_process");

    function requireOrInstall(module) {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    process.exit(1007);
    }
    }

    const cluster = require("cluster");

    if (cluster.isMaster) {
    cluster.fork();
    cluster.on("exit", (worker, code, signal) => {
    if (code === 1007) {
    cluster.fork();
    }
    });
    } else if (cluster.isWorker) {
    // The real work here for the worker

    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = requireOrInstall("find-free-port");
    const express = requireOrInstall("express");
    const socket = requireOrInstall("socket.io");

    process.exit(0);
    }


    The idea here is to re-run the process in case of a missing module. This way we fully reproduce a manual npm install so as you guess it works! Also it seems more synchronous rather the first option, but a bit more complex.






    share|improve this answer


























    • I used your first solution. It's elegant ES6 code and it did the trick with only a couple of extra lines and without the use of third party packages (which was kind of the whole point for me). Thanks!

      – Gust van de Wal
      Nov 19 '18 at 20:52











    • I am glad that you used/liked my answer! My only concern is about await setTimeout(() => {}, 1000); Are you sure it can work like this? Without a promise? I cannot reproduce the delay that way, in chrome at least.

      – Alex Michailidis
      Nov 20 '18 at 7:00






    • 1





      I seem to have blindly pasted the setTimeout option in there after it worked in a test. I hadn't noticed it didn't actually wait 1 second. Good catch!

      – Gust van de Wal
      Nov 20 '18 at 9:07
















    1





    +150









    It seems that the require operation after an npm install needs a certain delay.
    Also the problem is worse in windows, it will always fail if the module needs to be npm installed.
    It's like at a specific event snapshot is already known what modules can be required and what cannot. Probably that's why require.cache was mentioned in the comments. Nevertheless I suggest you to check the 2 following solutions.



    1) Use a delay



    const cp = require("child_process");

    const requireOrInstall = async module => {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    // Use one of the two awaits below
    // The first one waits 1000 milliseconds
    // The other waits until the next event cycle
    // Both work
    await new Promise(resolve => setTimeout(() => resolve(), 1000));
    await new Promise(resolve => setImmediate(() => resolve()));
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    }
    }

    const main = async() => {
    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = await requireOrInstall("find-free-port");
    const express = await requireOrInstall("express");
    const socket = await requireOrInstall("socket.io");
    }

    main();


    await always needs a promise to work with, but it's not needed to explicitly create one as await will wrap whatever it is waiting for in a promise if it isn't handed one.



    2) Use a cluster



    const cp = require("child_process");

    function requireOrInstall(module) {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    process.exit(1007);
    }
    }

    const cluster = require("cluster");

    if (cluster.isMaster) {
    cluster.fork();
    cluster.on("exit", (worker, code, signal) => {
    if (code === 1007) {
    cluster.fork();
    }
    });
    } else if (cluster.isWorker) {
    // The real work here for the worker

    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = requireOrInstall("find-free-port");
    const express = requireOrInstall("express");
    const socket = requireOrInstall("socket.io");

    process.exit(0);
    }


    The idea here is to re-run the process in case of a missing module. This way we fully reproduce a manual npm install so as you guess it works! Also it seems more synchronous rather the first option, but a bit more complex.






    share|improve this answer


























    • I used your first solution. It's elegant ES6 code and it did the trick with only a couple of extra lines and without the use of third party packages (which was kind of the whole point for me). Thanks!

      – Gust van de Wal
      Nov 19 '18 at 20:52











    • I am glad that you used/liked my answer! My only concern is about await setTimeout(() => {}, 1000); Are you sure it can work like this? Without a promise? I cannot reproduce the delay that way, in chrome at least.

      – Alex Michailidis
      Nov 20 '18 at 7:00






    • 1





      I seem to have blindly pasted the setTimeout option in there after it worked in a test. I hadn't noticed it didn't actually wait 1 second. Good catch!

      – Gust van de Wal
      Nov 20 '18 at 9:07














    1





    +150







    1





    +150



    1




    +150





    It seems that the require operation after an npm install needs a certain delay.
    Also the problem is worse in windows, it will always fail if the module needs to be npm installed.
    It's like at a specific event snapshot is already known what modules can be required and what cannot. Probably that's why require.cache was mentioned in the comments. Nevertheless I suggest you to check the 2 following solutions.



    1) Use a delay



    const cp = require("child_process");

    const requireOrInstall = async module => {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    // Use one of the two awaits below
    // The first one waits 1000 milliseconds
    // The other waits until the next event cycle
    // Both work
    await new Promise(resolve => setTimeout(() => resolve(), 1000));
    await new Promise(resolve => setImmediate(() => resolve()));
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    }
    }

    const main = async() => {
    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = await requireOrInstall("find-free-port");
    const express = await requireOrInstall("express");
    const socket = await requireOrInstall("socket.io");
    }

    main();


    await always needs a promise to work with, but it's not needed to explicitly create one as await will wrap whatever it is waiting for in a promise if it isn't handed one.



    2) Use a cluster



    const cp = require("child_process");

    function requireOrInstall(module) {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    process.exit(1007);
    }
    }

    const cluster = require("cluster");

    if (cluster.isMaster) {
    cluster.fork();
    cluster.on("exit", (worker, code, signal) => {
    if (code === 1007) {
    cluster.fork();
    }
    });
    } else if (cluster.isWorker) {
    // The real work here for the worker

    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = requireOrInstall("find-free-port");
    const express = requireOrInstall("express");
    const socket = requireOrInstall("socket.io");

    process.exit(0);
    }


    The idea here is to re-run the process in case of a missing module. This way we fully reproduce a manual npm install so as you guess it works! Also it seems more synchronous rather the first option, but a bit more complex.






    share|improve this answer















    It seems that the require operation after an npm install needs a certain delay.
    Also the problem is worse in windows, it will always fail if the module needs to be npm installed.
    It's like at a specific event snapshot is already known what modules can be required and what cannot. Probably that's why require.cache was mentioned in the comments. Nevertheless I suggest you to check the 2 following solutions.



    1) Use a delay



    const cp = require("child_process");

    const requireOrInstall = async module => {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    // Use one of the two awaits below
    // The first one waits 1000 milliseconds
    // The other waits until the next event cycle
    // Both work
    await new Promise(resolve => setTimeout(() => resolve(), 1000));
    await new Promise(resolve => setImmediate(() => resolve()));
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    }
    }

    const main = async() => {
    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = await requireOrInstall("find-free-port");
    const express = await requireOrInstall("express");
    const socket = await requireOrInstall("socket.io");
    }

    main();


    await always needs a promise to work with, but it's not needed to explicitly create one as await will wrap whatever it is waiting for in a promise if it isn't handed one.



    2) Use a cluster



    const cp = require("child_process");

    function requireOrInstall(module) {
    try {
    require.resolve(module);
    } catch (e) {
    console.log(`Could not resolve "${module}"nInstalling`);
    cp.execSync(`npm install ${module}`);
    console.log(`"${module}" has been installed`);
    }
    console.log(`Requiring "${module}"`);
    try {
    return require(module);
    } catch (e) {
    console.log(require.cache);
    console.log(e);
    process.exit(1007);
    }
    }

    const cluster = require("cluster");

    if (cluster.isMaster) {
    cluster.fork();
    cluster.on("exit", (worker, code, signal) => {
    if (code === 1007) {
    cluster.fork();
    }
    });
    } else if (cluster.isWorker) {
    // The real work here for the worker

    const http = require("http");
    const path = require("path");
    const fs = require("fs");
    const ffp = requireOrInstall("find-free-port");
    const express = requireOrInstall("express");
    const socket = requireOrInstall("socket.io");

    process.exit(0);
    }


    The idea here is to re-run the process in case of a missing module. This way we fully reproduce a manual npm install so as you guess it works! Also it seems more synchronous rather the first option, but a bit more complex.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 20 '18 at 9:43

























    answered Nov 19 '18 at 15:26









    Alex MichailidisAlex Michailidis

    2,1501519




    2,1501519













    • I used your first solution. It's elegant ES6 code and it did the trick with only a couple of extra lines and without the use of third party packages (which was kind of the whole point for me). Thanks!

      – Gust van de Wal
      Nov 19 '18 at 20:52











    • I am glad that you used/liked my answer! My only concern is about await setTimeout(() => {}, 1000); Are you sure it can work like this? Without a promise? I cannot reproduce the delay that way, in chrome at least.

      – Alex Michailidis
      Nov 20 '18 at 7:00






    • 1





      I seem to have blindly pasted the setTimeout option in there after it worked in a test. I hadn't noticed it didn't actually wait 1 second. Good catch!

      – Gust van de Wal
      Nov 20 '18 at 9:07



















    • I used your first solution. It's elegant ES6 code and it did the trick with only a couple of extra lines and without the use of third party packages (which was kind of the whole point for me). Thanks!

      – Gust van de Wal
      Nov 19 '18 at 20:52











    • I am glad that you used/liked my answer! My only concern is about await setTimeout(() => {}, 1000); Are you sure it can work like this? Without a promise? I cannot reproduce the delay that way, in chrome at least.

      – Alex Michailidis
      Nov 20 '18 at 7:00






    • 1





      I seem to have blindly pasted the setTimeout option in there after it worked in a test. I hadn't noticed it didn't actually wait 1 second. Good catch!

      – Gust van de Wal
      Nov 20 '18 at 9:07

















    I used your first solution. It's elegant ES6 code and it did the trick with only a couple of extra lines and without the use of third party packages (which was kind of the whole point for me). Thanks!

    – Gust van de Wal
    Nov 19 '18 at 20:52





    I used your first solution. It's elegant ES6 code and it did the trick with only a couple of extra lines and without the use of third party packages (which was kind of the whole point for me). Thanks!

    – Gust van de Wal
    Nov 19 '18 at 20:52













    I am glad that you used/liked my answer! My only concern is about await setTimeout(() => {}, 1000); Are you sure it can work like this? Without a promise? I cannot reproduce the delay that way, in chrome at least.

    – Alex Michailidis
    Nov 20 '18 at 7:00





    I am glad that you used/liked my answer! My only concern is about await setTimeout(() => {}, 1000); Are you sure it can work like this? Without a promise? I cannot reproduce the delay that way, in chrome at least.

    – Alex Michailidis
    Nov 20 '18 at 7:00




    1




    1





    I seem to have blindly pasted the setTimeout option in there after it worked in a test. I hadn't noticed it didn't actually wait 1 second. Good catch!

    – Gust van de Wal
    Nov 20 '18 at 9:07





    I seem to have blindly pasted the setTimeout option in there after it worked in a test. I hadn't noticed it didn't actually wait 1 second. Good catch!

    – Gust van de Wal
    Nov 20 '18 at 9:07













    1














    I think your best option is either:




    • (ugly) to install package globally, instead of locally

    • (best solution ?) to define YOUR new 'package repository installation', when installing, AND when requiring


    First, you may consider using the npm-programmatic package.



    Then, you may define your repository path with something like:



    const PATH='/tmp/myNodeModuleRepository';


    Then, replace your installation instruction with something like:



    const npm = require('npm-programmatic');
    npm.install(`${module}`, {
    cwd: PATH,
    save:true
    }


    Eventually, replace your failback require instruction, with something like:



    return require(module, { paths: [ PATH ] });


    If it is still not working, you may update the require.cache variable, for instance to invalide a module, you can do something like:



    delete require.cache[process.cwd() + 'node_modules/bluebird/js/release/bluebird.js'];


    You may need to update it manually, to add information about your new module, before loading it.






    share|improve this answer




























      1














      I think your best option is either:




      • (ugly) to install package globally, instead of locally

      • (best solution ?) to define YOUR new 'package repository installation', when installing, AND when requiring


      First, you may consider using the npm-programmatic package.



      Then, you may define your repository path with something like:



      const PATH='/tmp/myNodeModuleRepository';


      Then, replace your installation instruction with something like:



      const npm = require('npm-programmatic');
      npm.install(`${module}`, {
      cwd: PATH,
      save:true
      }


      Eventually, replace your failback require instruction, with something like:



      return require(module, { paths: [ PATH ] });


      If it is still not working, you may update the require.cache variable, for instance to invalide a module, you can do something like:



      delete require.cache[process.cwd() + 'node_modules/bluebird/js/release/bluebird.js'];


      You may need to update it manually, to add information about your new module, before loading it.






      share|improve this answer


























        1












        1








        1







        I think your best option is either:




        • (ugly) to install package globally, instead of locally

        • (best solution ?) to define YOUR new 'package repository installation', when installing, AND when requiring


        First, you may consider using the npm-programmatic package.



        Then, you may define your repository path with something like:



        const PATH='/tmp/myNodeModuleRepository';


        Then, replace your installation instruction with something like:



        const npm = require('npm-programmatic');
        npm.install(`${module}`, {
        cwd: PATH,
        save:true
        }


        Eventually, replace your failback require instruction, with something like:



        return require(module, { paths: [ PATH ] });


        If it is still not working, you may update the require.cache variable, for instance to invalide a module, you can do something like:



        delete require.cache[process.cwd() + 'node_modules/bluebird/js/release/bluebird.js'];


        You may need to update it manually, to add information about your new module, before loading it.






        share|improve this answer













        I think your best option is either:




        • (ugly) to install package globally, instead of locally

        • (best solution ?) to define YOUR new 'package repository installation', when installing, AND when requiring


        First, you may consider using the npm-programmatic package.



        Then, you may define your repository path with something like:



        const PATH='/tmp/myNodeModuleRepository';


        Then, replace your installation instruction with something like:



        const npm = require('npm-programmatic');
        npm.install(`${module}`, {
        cwd: PATH,
        save:true
        }


        Eventually, replace your failback require instruction, with something like:



        return require(module, { paths: [ PATH ] });


        If it is still not working, you may update the require.cache variable, for instance to invalide a module, you can do something like:



        delete require.cache[process.cwd() + 'node_modules/bluebird/js/release/bluebird.js'];


        You may need to update it manually, to add information about your new module, before loading it.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 19 '18 at 14:22









        Bsquare ℬℬBsquare ℬℬ

        3,664101635




        3,664101635























            0














            cp.execSync is an async call so try check if the module is installed in it's call back function. I have tried it, installation is clean now:






            const cp = require('child_process')

            function requireOrInstall (module) {
            try {
            require.resolve(module)
            } catch (e) {
            console.log(`Could not resolve "${module}"nInstalling`)
            cp.execSync(`npm install ${module}`, () => {
            console.log(`"${module}" has been installed`)
            try {
            return require(module)
            } catch (e) {
            console.log(require.cache)
            console.log(e)
            }
            })

            }
            console.log(`Requiring "${module}"`)

            }

            const http = require('http')
            const path = require('path')
            const fs = require('fs')
            const ffp = requireOrInstall('find-free-port')
            const express = requireOrInstall('express')
            const socket = requireOrInstall('socket.io')






            When node_modules not available yet :
            enter image description here



            When node_modules available already:
            enter image description here







            share|improve this answer


























            • .exec() is asynchronous, not .execSync(). it therefore doesn't have a callback function. Returning the required module also won't work here, because you're returning the require from inside the callback, not requireOrInstall(). It's not the installation that fails, but the modules are unfindable.

              – Gust van de Wal
              Nov 19 '18 at 20:18


















            0














            cp.execSync is an async call so try check if the module is installed in it's call back function. I have tried it, installation is clean now:






            const cp = require('child_process')

            function requireOrInstall (module) {
            try {
            require.resolve(module)
            } catch (e) {
            console.log(`Could not resolve "${module}"nInstalling`)
            cp.execSync(`npm install ${module}`, () => {
            console.log(`"${module}" has been installed`)
            try {
            return require(module)
            } catch (e) {
            console.log(require.cache)
            console.log(e)
            }
            })

            }
            console.log(`Requiring "${module}"`)

            }

            const http = require('http')
            const path = require('path')
            const fs = require('fs')
            const ffp = requireOrInstall('find-free-port')
            const express = requireOrInstall('express')
            const socket = requireOrInstall('socket.io')






            When node_modules not available yet :
            enter image description here



            When node_modules available already:
            enter image description here







            share|improve this answer


























            • .exec() is asynchronous, not .execSync(). it therefore doesn't have a callback function. Returning the required module also won't work here, because you're returning the require from inside the callback, not requireOrInstall(). It's not the installation that fails, but the modules are unfindable.

              – Gust van de Wal
              Nov 19 '18 at 20:18
















            0












            0








            0







            cp.execSync is an async call so try check if the module is installed in it's call back function. I have tried it, installation is clean now:






            const cp = require('child_process')

            function requireOrInstall (module) {
            try {
            require.resolve(module)
            } catch (e) {
            console.log(`Could not resolve "${module}"nInstalling`)
            cp.execSync(`npm install ${module}`, () => {
            console.log(`"${module}" has been installed`)
            try {
            return require(module)
            } catch (e) {
            console.log(require.cache)
            console.log(e)
            }
            })

            }
            console.log(`Requiring "${module}"`)

            }

            const http = require('http')
            const path = require('path')
            const fs = require('fs')
            const ffp = requireOrInstall('find-free-port')
            const express = requireOrInstall('express')
            const socket = requireOrInstall('socket.io')






            When node_modules not available yet :
            enter image description here



            When node_modules available already:
            enter image description here







            share|improve this answer















            cp.execSync is an async call so try check if the module is installed in it's call back function. I have tried it, installation is clean now:






            const cp = require('child_process')

            function requireOrInstall (module) {
            try {
            require.resolve(module)
            } catch (e) {
            console.log(`Could not resolve "${module}"nInstalling`)
            cp.execSync(`npm install ${module}`, () => {
            console.log(`"${module}" has been installed`)
            try {
            return require(module)
            } catch (e) {
            console.log(require.cache)
            console.log(e)
            }
            })

            }
            console.log(`Requiring "${module}"`)

            }

            const http = require('http')
            const path = require('path')
            const fs = require('fs')
            const ffp = requireOrInstall('find-free-port')
            const express = requireOrInstall('express')
            const socket = requireOrInstall('socket.io')






            When node_modules not available yet :
            enter image description here



            When node_modules available already:
            enter image description here







            const cp = require('child_process')

            function requireOrInstall (module) {
            try {
            require.resolve(module)
            } catch (e) {
            console.log(`Could not resolve "${module}"nInstalling`)
            cp.execSync(`npm install ${module}`, () => {
            console.log(`"${module}" has been installed`)
            try {
            return require(module)
            } catch (e) {
            console.log(require.cache)
            console.log(e)
            }
            })

            }
            console.log(`Requiring "${module}"`)

            }

            const http = require('http')
            const path = require('path')
            const fs = require('fs')
            const ffp = requireOrInstall('find-free-port')
            const express = requireOrInstall('express')
            const socket = requireOrInstall('socket.io')





            const cp = require('child_process')

            function requireOrInstall (module) {
            try {
            require.resolve(module)
            } catch (e) {
            console.log(`Could not resolve "${module}"nInstalling`)
            cp.execSync(`npm install ${module}`, () => {
            console.log(`"${module}" has been installed`)
            try {
            return require(module)
            } catch (e) {
            console.log(require.cache)
            console.log(e)
            }
            })

            }
            console.log(`Requiring "${module}"`)

            }

            const http = require('http')
            const path = require('path')
            const fs = require('fs')
            const ffp = requireOrInstall('find-free-port')
            const express = requireOrInstall('express')
            const socket = requireOrInstall('socket.io')






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 19 '18 at 20:14

























            answered Nov 19 '18 at 16:26









            Arulmozhi ManikandanArulmozhi Manikandan

            1918




            1918













            • .exec() is asynchronous, not .execSync(). it therefore doesn't have a callback function. Returning the required module also won't work here, because you're returning the require from inside the callback, not requireOrInstall(). It's not the installation that fails, but the modules are unfindable.

              – Gust van de Wal
              Nov 19 '18 at 20:18





















            • .exec() is asynchronous, not .execSync(). it therefore doesn't have a callback function. Returning the required module also won't work here, because you're returning the require from inside the callback, not requireOrInstall(). It's not the installation that fails, but the modules are unfindable.

              – Gust van de Wal
              Nov 19 '18 at 20:18



















            .exec() is asynchronous, not .execSync(). it therefore doesn't have a callback function. Returning the required module also won't work here, because you're returning the require from inside the callback, not requireOrInstall(). It's not the installation that fails, but the modules are unfindable.

            – Gust van de Wal
            Nov 19 '18 at 20:18







            .exec() is asynchronous, not .execSync(). it therefore doesn't have a callback function. Returning the required module also won't work here, because you're returning the require from inside the callback, not requireOrInstall(). It's not the installation that fails, but the modules are unfindable.

            – Gust van de Wal
            Nov 19 '18 at 20:18




















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270058%2fnode-cant-find-certain-modules-after-synchronous-install%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Bressuire

            Vorschmack

            Quarantine