8

I have some NPM scripts on a project i recently took over that go like this:

{
    "package": "yarn package-common && mv './dist/APP® TV.wgt' ''./package/$(yarn -s filename)''",
    "package-common": "tizen package -t wgt -s APP -- ./dist && rimraf package && mkdir package",
    "filename": "cross-env-shell echo APP_${npm_package_version}.wgt",
}

This project was written by someone on a MAC. How can i translate the following part into a functioning Powershell / cmd command? I havent found anything anywhere about this. It runs the command and the echoed value gets appended to the string it got called from.

''./package/$(yarn -s filename)''

Or even better, is there a way besides this package to do it cross-platform?

Currently the message that gets output in powershell when running yarn package is:

'' was unexpected at this time.
mklement0
  • 382,024
  • 64
  • 607
  • 775
IvanSt
  • 360
  • 4
  • 17
  • As an aside: The error message comes from `cmd.exe`, which npm uses by default on Windows. – mklement0 Mar 22 '19 at 15:51
  • If cross-platform is a requirement I suggest rewriting it all in a _node.js_ script and _shell-out_ the necessary commands using [`execSync`](https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options) (or [`exec`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) if you want asynchronous). You can then invoke the _node.js_ script via an npm-script, i,e. `"package": "node somescript.js"` – RobC Mar 22 '19 at 17:08
  • Or evaluate (i.e. `node -e ...`) the _node.js_ script inline from your npm script. E.g. `"package": "yarn package-common && node -e \"var sh = require('child_process').execSync; var fn = sh('yarn -s filename', {encoding: 'utf8'}); sh('mv ./dist/APP®TV.wgt ./package/' + fn);\"",` This will run cross-platform, however the most problematic part in your script is the combination of registered symbol plus space char in the path `./dist/APP® TV.wgt`. It's an encoding nightmare with _cmd.exe_. I suggest you change it (if you can) to e.g. `./dist/APP®TV.wgt ` as per the aforementioned npm script. – RobC Mar 23 '19 at 11:14
  • For running in a Windows only environment using the npm default _cmd.exe_ you may want to consider changing your `package` script to: `"package": "yarn package-common && FOR /F %f IN ('yarn -s filename') DO mv ./dist/\"APP®TV.wgt\" ./package/%~f",` - this also will entail changing the `./dist/APP® TV.wgt` path to `./dist/APP®TV.wgt`, i.e. omitting the combination of registered symbol plus space character to negate the encoding nightmare. – RobC Mar 23 '19 at 11:30

2 Answers2

12

Your basic choices for going cross-platform without platform-specific scripts are:

  • Use Bash also on Windows:

    • Run your npm scripts from Bash via WSL and use the existing Bash commands typically contained in package.json files, such as in your case.

    • Alternatively, with Git for Windows installed, configure npm to use bash.exe as the shell for invoking commands - see this answer [npm v5.1+].

  • Install PowerShell (Core) on all your platforms (including Windows), and define the commands as PowerShell commands (see below) [npm v5.1+].

Note the npm version version requirement (version 5.1 or higher) where noted, due to the configuration option to use a specific shell (script-shell) not being available in earlier versions. Run npm -v to get the installed version, and
npm install -g npm to update, if possible.


Using npm with PowerShell (Core) on all platforms [npm v5.1+]:

  • Install PowerShell Core.

  • Then configure npm to use PowerShell (Core) as the shell (requires npm version 5.1 or higher):

    • For all your projects (current user), or globally (all users):

      npm config set script-shell pwsh [--global]
      
    • For a given project only (from that project's root directory):

      npm config set script-shell pwsh --userconfig ./.npmrc
      
  • Finally, in your projects' package.json's scripts key, define the commands as PowerShell commands.

In the case at hand:

For instance, translate this Bash command:

yarn package-common && mv './dist/APP® TV.wgt' "./package/$(yarn -s filename)"

Note: I've replaced '' with ", because the latter make more sense; as originally written, the '' are effectively discarded by Bash, and the result of command substitution $(yarn -s filename) could break the command if it contained whitespace.

to this PowerShell command (v7+):

yarn package-common && mi './dist/APP® TV.wgt' "./package/$(yarn -s filename)"

Note:

  • mi is a built-in alias for PowerShell's Move-Item cmdlet.

  • While it makes sense to call your scripts from PowerShell also, that's not a requirement - calling from cmd.exe or a batch file will work too.

  • To get started with PowerShell (Core), see Learning PowerShell; also, http://hyperpolyglot.org/shell juxtaposes the syntax of POSIX-like shells such as Bash with that of cmd.exe and PowerShell in concise, tabular form.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

I'm going to shamelessly promote my solution to this problem. I was facing the same issues and decided to write a little tool to fix this once and for all. The result is a small Bash interpreter that is able to work on all platforms where NodeJS can run, including Windows. As a nice bonus, it supports running tasks in parallel too.

In short, you can replace npm run x with bake x and it will work. All scripts specified after the command will run in parallel, and if you install @samvv/bake as a dependency you'll be able to use it in your npm scripts as well, like so:

{
  "scripts": {
     "watch:compile-tests": "tsc -w",
     "watch:tests": "ava --watch",
     "test": "ava",
     "lint": "tsc --noEmit",
     "prepare": "npm run lint && webpack --mode production",
     "serve": "webpack serve --mode development",
     "lint-while-testing": "bake lint test"
  }
}

You can find the source code here and you'll be able to install the package using npm install -g @samvv/bake. See this Medium post for more information on how it works and how to use it.

I really hope this is useful to someone!

samvv
  • 1,934
  • 1
  • 18
  • 26