I am testing my module with REPL like this:
repl.start({
input: process.stdin,
output: process.stdout
})
.context.MyModule = MyModule;
Is there a way to reload the module automatically, when I change and save it, without having to exit and run repl again?
You can use the chokidar module and force reload (you will lose runtime context in the module, but it should auto-reload).
var ctx = repl.start({
input: process.stdin,
output: process.stdout
})
.context;
ctx.MyModule = require('./mymodule');
chokidar.watch('.', {ignored: /[\/\\]\./}).on('all', function(event, path) {
delete require.cache['./mymodule'];
ctx.MyModule = require('./mymodule');
});
If that doesn't work, I'm happy to play with it a little and get a working solution.
edit: if it doesn't garbage-collect cleanly (there are any open handles/listeners), this will leak each time it reloads. You may need to add a 'clean-exit' function to MyModule to stop everything gracefully, and then call that inside the watch handler.
Related
I have a requirement where we need to set dll path based upon whether it is executing in production or in development environment. So I decided to place that value in environment variable and tried to achieve that using webpack.DefinePlugin({}).
Method 1:
webpack.config.json
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV' : JSON.stringify('production')
})
And then I tried to get that value in electron's main process, In my case elec.js
elec.js
const Electron = require("electron");
const app = require("electron");
var dllPath = "";
function createWindow() {
let win = new BrowserWindow({
width: 800,
height: 600,
title: "Test",
icon: "Test.ico"
});
win.setMenu(null);
win.loadURL(
url.format({
pathname: path.join(__dirname, "../renderer/index.html"),
protocol: "file:",
slashes: true
})
);
if (process.env.NODE_ENV ==='production') {
dllPath = path.join(
__dirname,
"./../../dll/test.dll"
);
} else {
dllPath = path.join(
__dirname,
"./../../../dll/test.dll"
);
}
}
app.on("ready", createWindow);
But problem is that when I try to access that value in createWindow() function it is undefined so flow always goes to else block.
Is there anything I am missing?
Method 2:
I tried to achieve the same using cross-env node package, but no luck. Please find below code block which I tried using cross-env.
package.json
"scripts": {
"build": "cross-env process.env.NODE_ENV=production && rimraf ./dist/ && webpack --progress && node-sass
./src/renderer/scss/ -o ./dist/renderer/ && rimraf ./dist/renderer/includes/"
}
The problem is multi-faceted.
First, your elec.js is executed by Electron before the app is loaded. Electron runs elec.js, which creates the Browser window (let win = new BrowserWindow(...)) and loads HTML file (win.loadURL(...)) into it inside the browser process, the HTML then loads your webpack'ed js. So none of the webpacked js code is available in the elec.js. The webpack'ed code is also running in another process than the elec.js.
Another thing to note is that webpack plugin does not create any assignment to the variable it points too. It is done by simple text search and replace, in your example, all instances of process.env.NODE_ENV will be replaced with "production" string in the source code that is webpack'ed. That is not too obvious, but messes up the expected results.
One last thing - webpack plugin does not change any code in elec.js file, as that file is not webpack'ed.
So all that makes process.env.NODE_ENV from the build/webpack time not available in the elec.js code.
Once the mechanisms are clear, there are few ways to solve the problem, I will give general ideas, as there are plenty of discussions on each, and depending on circumstances and desired use case, some are better than others:
Generate a js file with necessary assignments based on environment variable during build (e.g. copy one of env-prod.js / env-dev.js -> env.js), copy it next to the elec.js, and reference it (require(env.js)) in elec.js.
Pass environment variable from command line (e.g. NODE_ENV=1 electron .) - it will get to elec.js.
Include a file into webpack based on environment variable (e.g. copy one of env-prod.js / env-dev.js -> env.js) and peek into webpacked' files from elec.js, e.g. using asar commands.
Use different version in package.json depending on build (e.g. version: "1.0.0-DEBUG" for debug), and read & parse it by calling app.getVersion() in elec.js. It is tricky as package.json should be a single file, but OS commands could be used (e.g. in "scripts") to copy one of prepared package.json files before invoking npm.
Here are some links that could help too:
Electron issue #7714 - discussion on relevant features in Electron
electron-is-dev - module checking if it is in dev
Electron boilerplate - example boilerplate that uses config/env-prod/dev files
The insight provided by iva2k is what allowed me to come to a solution for this same problem.
Using dotenv to create a .env file for my config got me halfway to where I wanted to be (setting up a few environment variables for use in a production setting). The problem then became that Electron wasn't passing those from the Main process down to the Renderer process by default.
The work-around is simple: use Electron's own ipcMain and ipcRenderer modules to pass the dotenv object between the two.
In your main file (e.g. your elec.js file), place an ipcMain event listener after requiring the module:
const config = require('dotenv').config();
const electron = require('electron');
const { app, BrowserWindow, ipcMain } = electron;
...
ipcMain.on('get-env', (event) => {
event.sender.send('get-env-reply', config);
});
Elsewhere, in your application's rendering-side, place this anywhere necessary:
async function getConfig()
{
const { ipcRenderer } = window.require('electron');
let config = null;
ipcRenderer.on('get-env-reply', (event, arg) => {
// The dotenv config object should return an object with
// another object inside caled "parsed". Change this if need be.
config = arg.parsed;
});
ipcRenderer.send('get-env');
return config;
}
This basically allowed me to declare one event in the Main process file, and then re-use it in any process-side file I wanted, thus allowing me to obfuscate config variables in a file that goes with the build, but isn't accessible to end-users without opening up the dev-tools.
Maybe late but can use simple hack in elec.js
const isProduction = process.env.NODE_ENV === 'production' || (!process || !process.env || !process.env.NODE_ENV);
In your console
For Windows
set MY_VARIABLE=true
For linux
$ export MY_VARIABLE=true
window.process.env.MY_VARIABLE
I've been refactoring an existing site towards using gulp and npm more efficiently. I'd like to incorporate inquirer into my tasks to define some build alternatives. For now I'm starting simple.
Here is my current gulpfile:
//Define our required components
var gulp = require('gulp');
var inq = require('inquirer');
var plugins = require('gulp-load-plugins')({ scope: ['dependencies', 'devDependencies'] });
//Function that retrieves the file for the specifically names task
function getTask(task) {
return require('./gulp-tasks/' + task)(gulp, plugins);
};
//Call for our tasks
gulp.task('thirdPartyComponents', getTask('ThirdPartyComponents'));
gulp.task('thirdPartyComponents.css', getTask('ThirdPartyComponentsCss'));
gulp.task('buyer.app', getTask('Buyer.App'));
gulp.task('buyer.controllers', getTask('Buyer.Controllers'));
gulp.task('buyer.directives', getTask('Buyer.Directives'));
gulp.task('buyer.filters', getTask('Buyer.Filters'));
//CODE NOTE: Dependent tasks are case-sensitive
gulp.task('default', function () {
var questions =
[
{
type: 'confirm',
name: 'minimize',
message: 'Do you want to minimize the files?',
default: true
}
];
inq.prompt(questions).then(function (answers) {
console.log(answers);
});
});
When I run the task I get this in the output window in the task explorer
[16:21:21] Using gulpfile ....
[16:21:21] Starting 'default'...
[16:21:21] 'default' errored after 185 ms
[16:21:21] Error: Implement me. Unknown stdin file type!
at process.stdin (node.js:740:17)
at setupReadlineOptions (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\inquirer\lib\ui\baseUI.js:57:35)
at module.exports (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\inquirer\lib\ui\baseUI.js:14:40)
at new module.exports (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\inquirer\lib\ui\prompt.js:15:8)
at Object.promptModule [as prompt] (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\inquirer\lib\inquirer.js:26:14)
at Gulp.<anonymous> (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\Gulpfile.js:31:9)
at module.exports (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\orchestrator\lib\runTask.js:34:7)
at Gulp.Orchestrator._runTask (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\orchestrator\index.js:273:3)
at Gulp.Orchestrator._runStep (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\orchestrator\index.js:214:10)
at Gulp.Orchestrator.start (C:\Projects\BuyerSupplier\BuyerSupplier\Main\Source\BuyerSupplier.site-dev\src\BuyerSupplier.site\node_modules\orchestrator\index.js:134:8)
Process terminated with code 1.
I'm not sure why something this straightforward doesnt want to fire off....any help is really appreciated.
Found the issue, and its nothing to do with the code directly. I am running inside Visual Studio and was using the Task Runner Explorer to call the default task. This was where I was seeing the issue.
After doing more research, and on a whim, I ran the gulp task from a command window. It completed as expected. Therefore there are still issues within the Task runner with commands that use process.stdin command.
I'm doing my baby steps in node.js, and i'm trying to understand sandbox mechanism.
Currently i'm using node v4.0.0 and node-inspector v0.12.3.
I've installed gf3/sandbox module and run it with this simple code:
var s = new Sandbox();
s.run('1 + 1 + " apples"',function(output) {
console.log(output.result);
});
In order to debug easily, i've also commented the timeout function in sandbox.js file:
// timer = setTimeout(function() {
// self.child.stdout.removeListener('output', output);
// stdout = JSON.stringify({ result: 'TimeoutError', console: [] });
// self.child.kill('SIGKILL');
// }, self.options.timeout);
The issue is that debug DOESN'T break on ANY line code of shovel.js, and i'm 100% sure the module is using its code.
Why is that ? And what can I do in order to debug shovel.js?
sandbox.js is spawning shovel.js as child process without debugging enabled(e.g. no --debug option). So the child process executes normally and your breakpoints are simply ignored. You need to start child process in debug mode too.
If you want to debug both sandbox.js and shovel.js at the same time, then use different debug ports. I'm not sure about node-inspector, but here is an example of how you can do it with the debugger module. I'm sure you can tweak a bit to make it work with node-inspector.
Comment the timeout code like you already did
Pass debug option while spawning child process in sandbox.js. Note the port is 5859:
self.child = spawn(this.options.node, ['--debug-brk=5859',this.options.shovel], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
start example.js in debug mode. By default, it starts at 5858 port:
node --debug-brk example.js
Now debug sandbox.js by connecting to 5858:
node debug localhost:5858
Once the child process starts, you can fire up separate terminal and start debugging shovel.js on port 5859:
node debug localhost:5859
For node-inspector, I think you need to use node-debug command instead of this.options.node for child process. Also there are options to set debug port explicitly.
From above, These could be the steps for node-inspector. Note: I haven't tested it:
Same as above
Open sandbox.js file and change this line like following to pass debug option while spawning child process. Note the port is 5859:
self.child = spawn('node-debug', ['--debug-port=5859',this.options.shovel], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
start example.js in debug mode. By default, it starts at 5858 port:
node-debug example.js
Now head to the browser to debug parent process:
http://127.0.0.1:8080/?ws=127.0.0.1:8080&port=5858
Once the child process starts, open up another browser window to debug shovel.js:
http://127.0.0.1:8080/?ws=127.0.0.1:8080&port=5859
How exactly would I go about testing a node-based CLI with Jasmine/jasmine-node? I have tested node modules in the past with Jasmine, which was easy, as I would merely require in the module in the spec file, initialise it and test it, but obviously that's different with a CLI. Obviously one method would be to convert it into a class and test it on its own, then convert it into a CLI, but that's not the real deal. Has anyone successfully tested a node CLI with Jasmine?
Like any good unix citizen, a node CLI app should support stdout redirection. If it does so, it then becomes pretty trivial to test it, by storing the buffer and running assertions on it when it ends. This testing strategy uses tape, but the general concepts of spawning a process and running assertions on its stdout should be transferable to another test framework.
var test = require('tape');
var spawn = require('child_process').spawn;
var path = require('path');
var read = require('fs').readFileSync;
test('binary', function (t) {
t.plan(3);
process.chdir(__dirname);
var ps = spawn(process.execPath, [
path.resolve(__dirname, '../bin/cmd.js'),
'fixture.txt'
]);
var out = '';
var err = '';
ps.stdout.on('data', function (buffer) { out += buffer; });
ps.stderr.on('data', function (buffer) { err += buffer; });
ps.on('exit', function (code) {
var expected = read('expected.txt', 'utf-8');
t.notOk(err, 'should not error');
t.equal(code, 0, 'should exit with code 0');
t.equal(out, expected, 'should perform the transform');
});
});
This CLI app takes a text file as its first parameter and runs it through a transform. All you have to do is provide a fixture and expected result and then run the test.
To run all tests in certain folder from CLI
On windows, from command prompt:
"<PATH TO NODE IF NOT IN ENV>\node.exe" "<PATH TO JASMINE-NODE MODULE>\jasmine-node\lib\jasmine-node\cli.js --verbose --test-dir <PATH TO SPECS>
Example:
"C:\Program Files (x86)\nodejs\node.exe" "c:\MyProject\node_modules\jasmine-node\lib\jasmine-node\cli.js" --verbose --test-dir c:\MyProject\Specs
Check docs on git:
https://github.com/mhevery/jasmine-node
https://github.com/mhevery/jasmine-node/wiki/Command-Line-Usage
When grunt.loadNpmTasks is used, a grunt task is automatically available to the command line. It can be useful, but sometimes, I would like this task to be private, so it can be used whithin the Grunt file but not available to the command line.
Here is a contrived example. If I do :
module.exports = function(grunt) {
grunt.initConfig({
clean: {
test: ['test'],
release: ['release']
},
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.registerTask('build', 'Build the project.', function() {
console.log("building project");
});
grunt.registerTask('release', ['clean:release', 'build']);
};
... I can use the following command :
$ grunt release
However, this one is also available, and both clean:release and clean:test will be executed:
$ grunt clean
I do not want that. I want to control what can be called from the command line, since I may not have foreseen some undesirable effects if the user directly calls some tasks or subtasks.
I thought about registering a new clean task to supersedes the main one, and then choose what to call when clean is invoked (or to call nothing at all), but it does not work well since it cannot call the original clean task:
grunt.registerTask('clean', ['clean:release']);
Use grunt.task.renameTask
var ticks = +new Date();
var clean = 'clean-' + ticks;
grunt.task.renameTask('clean', clean);
grunt.registerTask('release', [clean + ':release', 'build']);
grunt.config.set(clean, grunt.config.get('clean'));
Copying the configuration over is important if you want to preserve the targets configuration