ES6 import module inside node execution script - node.js

I'm trying to execute a node script inside package.json which looks like this.
"parse-xlsx": "node ./services/orders-parsing/xlsx-parser executeXlsxParsing ./private/testdata/$FILENAME"
So when I run FILENAME=unfried-xlsx-2.xlsx npm run parse-xlsx for exemple, it executes this:
import XlsxParser from "./executeExcelParsing";
const executeXlsxParsing = () => {
const xlsxParser = new XlsxParser({ fileName: process.argv.slice(2)[1] })
xlsxParser.executeParsing()
}
export default executeXlsxParsing;
But when I do so, I got this error
SyntaxError: Cannot use import statement outside a module
I did try to flag with --input-type but did not work and it outputs the same error than the one above:
"parse-xlsx": "node --input-type=module ./services/orders-parsing/xlsx-parser executeXlsxParsing ./private/testdata/$FILENAME"
For some reason I don't want to use type=module inside my package.json file.
Any workaround to be able to "force" the execution of this script with ES6 imports ?
Did try with latest LTS node 16.17.0 and my "old" version v12.22.1.
Thanks !

node --input-type only tells node how to parse string input you're passing it:
> node --help
Usage: node [options] [ script.js ] [arguments]
node inspect [options] [ script.js | host:port ] [arguments]
Options:
...
--input-type=... set module type for string input
...
However, you're telling it to run the file ./services/orders-parsing/xlsx-parser so that won't work.
Your only option (until Node changes how --input-type works) is to have a package.json with the type property set to module. Thankfully, you canhave lots of package.json files, and node will simply use the one closest to the file it's being asked to run, so you can add a minimal package.json inside your services/orders-parsing dir, and then make sure that's set to "type": "module", and now Node will run in ESM mode when asked to run files in the services/orders-parsing dir.
However, also note that if you want to pass arguments to xlsx-parser, you will need to use:
"parse-xlsx": "node ./services/orders-parsing/xlsx-parser -- executeXlsxParsing ./private/testdata/$FILENAME"
with those explicit -- in there, because:
> node --help
Usage: node [options] [ script.js ] [arguments]
node inspect [options] [ script.js | host:port ] [arguments]
Options:
- script read from stdin (default if no file name is
provided, interactive mode if a tty)
-- indicate the end of node options
If you don't add those -- then everything is considered options for the node executable instead of for whatever script you're running.

Related

How can I pass docker environment variables to an npm script?

As the title says...As a noobie I've given this a good attempt but can't seem to figure it out.
I have a dockerfile
FROM node:12
WORKDIR /app
COPY . /app
RUN npm config set registry https://registry.npmjs.org/
RUN npm install
EXPOSE 8080
ENTRYPOINT ["npm", "start",]
CMD [ "--", "-e=$ENVIRONMENT", "-t=$TESTS" ]
and a script in my package.json like so:
"scripts": {
"start": "node main.js"
}
Main.js is expecting two arguments. e & t.
I am struggling to pass these in to the container to then give to the script to run main.js (note there is a reason why im running it through a script ive just made this example simple)
To run my npm script I can do this:
npm start -- -e=abc -t=xyz
So I have tried this but no joy:
docker run -e ENVIRONMENT=abc -e TESTS=xyz myimage
Thanks
When you use the JSON form of CMD (or ENTRYPOINT or RUN), there is no interpolation at all; your script should literally see the string -e=$ENVIRONMENT as the argument. Instead you need to use the shell form, which will wrap this in a shell that expands environment variables. You can't do this with this particular split of ENTRYPOINT and CMD, but at the same time, it's not really necessary; just put the whole thing in CMD.
# No ENTRYPOINT
# No quoting; Docker wraps this in `sh -c ...`
CMD npm start -- -e="$ENVIRONMENT" -t="$TESTS"
You can also handle these directly in your application. The yargs library for example has a .env() function that allows environment variables to be used directly as options. You could also make process.env.TESTS be the default value for the option if it's not provided directly. This approach gets around the trouble of constructing (and possibly extending) a valid command line with the combination of arguments you need.

VS Code Run extension - no output shown from running program

Recently I have tried Deno and since it has native TypeScript support, I thought it would be neat to use it as a "typescript runner". For example in Node you would write node server.js and in Deno would be like deno run -A server.ts and all of this in the code runner extension.
In vscode settings.json :
"code-runner.executorMap": {
"javascript": "node",
"typescript": "cd $dir && deno run -A $fileName"
}
Consider the following ts file "index.ts" :
const add = (a: number,b: number):number =>{
console.log(a+b);
return a+b;
}
add(1,2);
Output when running using the extension :
[Running] deno run -A index.ts
[33m3[39m
[Done] exited with code=0 in 0.082 seconds
Output when using the terminal cmd/powershell :
C:\Users\Oliver\Desktop\DenoPJ>deno run -A index.ts
3
Any thoughts about why it works inside the terminal and not inside the extension ?
I tried and could reproduce the problem.
When you change the settings under "Run Code configuration" to "Run in Terminal"
and then run the code again with AltCtrlN,
VSCode will switch to the Terminal window and you should see:
PS C:\Users\jps\source\deno> cd "c:\Users\jps\source\deno\" ; if ($?) { deno run -A index.ts }
3
Try to add flags to command (depends on your actions of application):
deno run --allow-net --allow-read --allow-write --allow-env index.ts

How to disable warnings when node is launched via a (global) shell script

I am building a CLI tool with node, and want to use the fs.promise API. However, when the app is launched, there's always an ExperimentalWarning, which is super annoying and messes up with the interaction prompts. How can I disable this warning/all warnings?
I'm testing this with the latest node v10 lts release on Windows 10.
To use the CLI tool globally, I have added this to my package.json file:
{
//...
"preferGlobal": true,
"bin": { "myapp" : "./index.js" }
//...
}
And have run npm link to link the ./index.js script. Then I am able to run the app globally simply with myapp.
After some research I noticed that there are generally 2 ways to disable the warnings:
set environmental variable NODE_NO_WARNINGS=1
call the script with node --no-warnings ./index.js
Although I was able to disable the warnings with the 2 methods above, there seems to be no way to do that while directly running myapp command.
The shebang I placed in the entrance script ./index.js is:
#!/usr/bin/env node
// my code...
I have also read other discussions on modifying the shebang, but haven't found a universal/cross-platform way to do this - to either pass argument to node itself, or set the env variable.
If I publish this npm package, it would be great if there's a way to make sure the warnings of this single package are disabled in advance, instead of having each individual user tweak their environment themselves. Is there any hidden npm package.json configs that allow this?
Any help would be greatly appreciated!
I am now using a launcher script to spawn a child_process to work around this limitation. Ugly, but it works with npm link, global installs and whatnot.
#!/usr/bin/env node
const { spawnSync } = require("child_process");
const { resolve } = require("path");
// Say our original entrance script is `app.js`
const cmd = "node --no-warnings " + resolve(__dirname, "app.js");
spawnSync(cmd, { stdio: "inherit", shell: true });
As it's kind of like a hack, I won't be using this method next time, and will instead be wrapping the original APIs in a promise manually, sticking to util.promisify, or using the blocking/sync version of the APIs.
I configured my test script like this:
"scripts": {
"test": "tsc && cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest"
},
Notice the NODE_NO_WARNINGS=1 part. It disables the warnings I was getting from setting NODE_OPTIONS=--experimental-vm-modules
Here's what I'm using to run node with a command line flag:
#!/bin/sh
_=0// "exec" "/usr/bin/env" "node" "--experimental-repl-await" "$0" "$#"
// Your normal Javascript here
The first line tells the shell to use /bin/sh to run the script. The second line is a bit magical. To the shell it's a variable assignment _=0// followed by "exec" ....
Node sees it as a variable assignment followed by a comment - so it's almost a nop apart from the side effect of assigning 0 to _.
The result is that when the shell reaches line 2 it will exec node (via env) with any command line options you need.
New answer: You can also catch emitted warnings in your script and choose which ones to prevent from being logged
const originalEmit = process.emit;
process.emit = function (name, data, ...args) {
if (
name === `warning` &&
typeof data === `object` &&
data.name === `ExperimentalWarning`
//if you want to only stop certain messages, test for the message here:
//&& data.message.includes(`Fetch API`)
) {
return false;
}
return originalEmit.apply(process, arguments);
};
Inspired by this patch to yarn

How to prepend text to a file as an npm script command

I'm writing a bookmarklet. I need to prepend "javascript:" to the compiled, minified JavaScript. I'm looking for a way to accomplish this using an NPM package.json script.
{
"scripts": {
"oar:transpile-typescript": "tsc --target es6 --lib dom,es6 ./OarBookmarklet/Oar.ts",
"oar:minify-javascript": "jsmin -o ./OarBookmarklet/oar.min.js ./OarBookmarklet/oar.js",
"oar:prepend-javascript": "[??? prepend `javascript:` to minified JavaScript ???]",
"oar": "run-s oar:transpile-typescript oar:minify-javascript oar:prepend-javascript",
"build": "run-s oar"
}
}
For a cross-platform solution utilize node.js and it's builtin fs.readFileSync(...) and fs.writeFileSync(...). This way it doesn't matter which shell your npm script runs in (sh, cmd.exe, bash, bash.exe, pwsh, ... )
To achieve this consider either of the following two solutions - they're essentially the same just different methods of application.
Solution A. Using a separate node.js script
Create the following script, lets save it as prepend.js in the root of the project directory, i.e. at the same level as where package.json resides.
prepend.js
const fs = require('fs');
const filepath = './OarBookmarklet/oar.min.js';
const data = fs.readFileSync(filepath);
fs.writeFileSync(filepath, 'javascript:' + data);
package.json
Define the oar:prepend-javascript npm script in package.json as follows::
"scripts": {
...
"oar:prepend-javascript": "node prepend",
...
},
Note: Above node.js invokes the script and performs the required task. If you choose to save prepend.js in a different directory than the aforementioned then ensure you define the correct path to it, i.e. "oar:prepend-javascript": "node ./some/other/path/to/prepend.js"
Solution B. Inline the node.js script in package.json
Alternatively, you can inline the content of prepend.js in your npm script, therefore negating the use of a separate .js file.
package.json
Define the oar:prepend-javascript script in package.json as follows:
"scripts": {
...
"oar:prepend-javascript": "node -e \"const fs = require('fs'); const fp = './OarBookmarklet/oar.min.js'; const d = fs.readFileSync(fp); fs.writeFileSync(fp, 'javascript:' + d);\""
...
},
Note: Here the nodejs command line option -e is utilized to evaluate the inline JavaScript.
If this is running on something Unix-like then:
(printf 'javascript:' ; cat ./OarBookmarklet/oar.min.js) > ./OarBookmarklet/oar.bm.min.js
should do the job.
Edit in response to OP's comment:
My execution environment is Windows, ...
In that case you should be able to use:
(set /p junk="javascript:" <nul & type ./OarBookmarklet/oar.min.js) > ./OarBookmarklet/oar.bm.min.js
The set /p ... <nul weirdness is a way to get some text sent to stdout without a newline being appended to it.

Is it possible to run an npm script in the current context using the source operator?

I'm trying to write a simple Node.js CLI to more easily navigate directories. For the sake of simplicity, let's say this is the CLI I'm trying to make:
test.js
#!/usr/bin/env node
console.log('hey there');
var exec = require('child_process').exec;
exec('cd ~/code/');
package.json
{
"name": "example",
"version": "0.1.0",
"description": "Example CLI that needs to run in current context",
"bin": {
"myScript": "test.js"
},
"engines": {
"node": "0.10.*",
"npm": "1.2.*"
}
}
With both of those in the same folder, running npm link will create the CLI. After that, if I run myScript, it outputs "hey there" but doesn't change directories. I know that this is because myScript is run in it's own subshell, which is subsequently terminated. I've read here about the source operator and found that it's used like such:
. filename [arguments]
I've tried doing . myScript to force my code to run in the current context. However, by using the source operator, the code is interpreted as bash instead of js. Here's the error I get:
-bash: /Users/dallinosmun/.nvm/v0.10.21/bin/myScript: line 3: syntax error near unexpected token `('
-bash: /Users/dallinosmun/.nvm/v0.10.21/bin/myScript: line 3: `var exec = require('child_process').exec;'
So, any ideas on how to get a Node.js CLI to run in the current context?
It is not possible since the exec command starts a new process.
But this is a link on a way to bypass the problem:
how do I make node child_process exec continuously

Resources