├── tests ├── macos.expected.txt ├── linux.expected.txt ├── linux.test.sh ├── macos.test.sh ├── windows.expected.txt ├── windows.configuration.json ├── package-lock.json ├── nix.test.bats ├── windows.test.ps1 ├── package.json └── README.md ├── .gitignore ├── LICENSE ├── package.json ├── .github └── workflows │ └── cicd.yaml ├── README.md ├── functions.js └── index.js /tests/macos.expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3.mac 4 | 4.mac 5 | 5.mac 6 | 6 7 | -------------------------------------------------------------------------------- /tests/linux.expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3.linux 4 | 4.linux 5 | 5.linux 6 | 6 7 | -------------------------------------------------------------------------------- /tests/linux.test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NIX_OS=linux node_modules/bats/bin/bats ./nix.test.bats -------------------------------------------------------------------------------- /tests/macos.test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NIX_OS=macos node_modules/bats/bin/bats ./nix.test.bats -------------------------------------------------------------------------------- /tests/windows.expected.txt: -------------------------------------------------------------------------------- 1 | '1' 2 | '2' 3 | '3.windows' 4 | '4.windows' 5 | '5.windows' 6 | '6' 7 | -------------------------------------------------------------------------------- /tests/windows.configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "Run": { 3 | "Exit": true, 4 | "Path": "./windows.test.ps1" 5 | } 6 | } -------------------------------------------------------------------------------- /tests/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "run-script-os-tests", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "bats": { 7 | "version": "1.2.1", 8 | "resolved": "https://registry.npmjs.org/bats/-/bats-1.2.1.tgz", 9 | "integrity": "sha512-2fcPDRQa/Kvh6j1IcCqsHpT5b9ObMzRzw6abC7Bg298PX8Qdh9VRkvO2WJUEhdyfjq2rLBCOAWdcv0tS4+xTUA==", 10 | "dev": true 11 | }, 12 | "run-script-os": { 13 | "version": "file:..", 14 | "dev": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Test results 40 | tests/*.actual.*.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Charlie Guse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/nix.test.bats: -------------------------------------------------------------------------------- 1 | #!node_modules/.bin/bats 2 | generate_results_filename () { 3 | local date_time=$(date -u +"%Y%m%d-%H%M%S") 4 | local uuid=$(uuidgen) 5 | echo "$NIX_OS.actual.$date_time._.$uuid.txt" 6 | } 7 | 8 | # It = run-scripts-os 9 | @test "It should run as expected with pre/post variables" { 10 | local expected="$NIX_OS.expected.txt" 11 | local results=$(generate_results_filename) 12 | 13 | run $(npm run-script sample --silent > $results) 14 | [ "$status" -eq 0 ] 15 | 16 | run diff --text $expected $results 17 | [ "$status" -eq 0 ] 18 | } 19 | 20 | @test "It should be able to error out as expected" { 21 | run npm run-script test-error --silent 22 | [ "$status" -eq 11 ] 23 | } 24 | 25 | @test "It should be able to be able to pass arguements along" { 26 | local expected="Hello, run-script-os." 27 | 28 | run npm run-script test-args --silent 29 | [ "$status" -eq 0 ] 30 | [ "$output" = "$expected" ] 31 | } 32 | 33 | @test "It should be able to use the darwin alias for MacOS" { 34 | local expected="darwin" 35 | 36 | if [ "$NIX_OS" != "macos" ] 37 | then 38 | expected="not darwin" 39 | fi 40 | 41 | run npm run-script test-darwin --silent 42 | [ "$status" -eq 0 ] 43 | [ "$output" = "$expected" ] 44 | } -------------------------------------------------------------------------------- /tests/windows.test.ps1: -------------------------------------------------------------------------------- 1 | 2 | Describe "run-script-os" { 3 | BeforeAll { 4 | function Get-GenerateResultsFilename { 5 | $date_time = Get-Date ([datetime]::UtcNow) -UFormat "%Y%m%d-%H%M%S" 6 | $guid = [guid]::newguid() 7 | "windows.actual.$date_time._.$guid.txt" 8 | } 9 | } 10 | 11 | It "should run as expected with pre/post variables" { 12 | $expected = "windows.expected.txt" 13 | $results = Get-GenerateResultsFilename 14 | 15 | npm run-script sample --silent > $results 16 | $Global:LASTEXITCODE | Should -Be 0 17 | 18 | $diff = Compare-Object -ReferenceObject (Get-Content $expected) -DifferenceObject (Get-Content $results) 19 | $diffCount = $diff | Measure-Object | Select-Object -ExpandProperty Count 20 | $diffCount | Should -Be 0 21 | } 22 | 23 | It "should be able to error out as expected" { 24 | npm run-script test-error --silent 25 | $Global:LASTEXITCODE | Should -Be 22 26 | } 27 | 28 | It "should be able to be able to pass arguements along" { 29 | $expected = """'Hello,'"" ""'run-script-os.'""" 30 | 31 | $output = npm run-script test-args --silent 32 | $output | Should -Be $expected 33 | } 34 | 35 | It "should be able to use the darwin alias for MacOS" { 36 | # This powershell test is only run against Windows currently. 37 | $expected = "'not darwin'" 38 | 39 | $output = npm run-script test-darwin --silent 40 | $output | Should -Be $expected 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "run-script-os-tests", 3 | "description": "run-script-os is a tool that will let you use generic npm script commands that will pass through to os specific commands.", 4 | "scripts": { 5 | "test": "run-script-os", 6 | "test:windows": "@powershell -NoProfile -Command \"Invoke-Pester -Configuration (Get-Content ./windows.configuration.json | ConvertFrom-Json) | Select-Object -ExpandProperty FailedCount | Should -BeNull\"", 7 | "test:linux": "./linux.test.sh", 8 | "test:darwin": "./macos.test.sh", 9 | 10 | "get-pester-defaults": "@powershell -NoProfile -Command \"Import-Module Pester; [PesterConfiguration]::Default\"", 11 | 12 | "presample": "echo '1'", 13 | "sample": "echo '2' && run-script-os", 14 | 15 | "presample:windows": "echo '3.windows'", 16 | "sample:windows": "echo '4.windows'", 17 | "postsample:windows": "echo '5.windows'", 18 | 19 | "presample:linux": "echo '3.linux'", 20 | "sample:linux": "echo '4.linux'", 21 | "postsample:linux": "echo '5.linux'", 22 | 23 | "presample:macos": "echo '3.mac'", 24 | "sample:macos": "echo '4.mac'", 25 | "postsample:macos": "echo '5.mac'", 26 | 27 | "postsample": "echo '6'", 28 | 29 | "test-error": "run-script-os", 30 | "test-error:nix": "exit 11", 31 | "test-error:win32": "exit 22", 32 | 33 | "test-args": "run-script-os 'Hello,' 'run-script-os.'", 34 | "test-args:default": "echo", 35 | 36 | "test-darwin": "run-script-os", 37 | "test-darwin:darwin": "echo 'darwin'", 38 | "test-darwin:default": "echo 'not darwin'" 39 | }, 40 | "author": "Charlie Guse (https://github.com/charlesguse/run-script-os)", 41 | "license": "MIT", 42 | "devDependencies": { 43 | "bats": "^1.2.1", 44 | "run-script-os": "file:.." 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "run-script-os", 3 | "version": "1.1.6", 4 | "description": "run-script-os is a tool that will let you use generic npm script commands that will pass through to os specific commands.", 5 | "main": "index.js", 6 | "scripts": { 7 | "example": "echo '0. Call either npm test, npm run test, or npm run-script test and check out the output to see how pre and post commands work with run-script-os'", 8 | "pretest": "echo '1. Pretest (Generic) called first when running npm test'", 9 | "test": "echo '2. Test (Generic) runs transitioning to OS specific code' && run-script-os", 10 | "pretest:win32": "echo '3. Pretest (Windows 32/64)'", 11 | "test:win32": "echo '4. Test (Windows 32/64)'", 12 | "posttest:win32": "echo '5. Posttest (Windows 32/64)'", 13 | "pretest:linux:darwin": "echo '3. Pretest (Linux/MacOS)'", 14 | "test:linux:darwin": "echo '4. Test (Linux/MacOS)'", 15 | "posttest:linux:darwin": "echo '5. Posttest (Linux/MacOS)'", 16 | "posttest": "echo '6. Posttest (Generic) is finally called after all of the OS specific commands are called'", 17 | "test-error": "run-script-os", 18 | "test-error:linux": "echo 'Run $? afterwards to confirm the error code is propagating appropriately.' && exit 11", 19 | "test-error:win32": "echo 'Run $LASTEXITCODE (PowerShell) or %errorlevel% (CMD) afterwards to confirm the error code is propagating appropriately.' && exit 22", 20 | "test-args": "run-script-os 'Hello,' 'run-script-os.'", 21 | "test-args:default": "echo" 22 | }, 23 | "author": "Charlie Guse (https://github.com/charlesguse/run-script-os)", 24 | "license": "MIT", 25 | "bin": { 26 | "run-script-os": "./index.js", 27 | "run-os": "./index.js" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/charlesguse/run-script-os.git" 32 | }, 33 | "files": [ 34 | "LICENSE", 35 | "functions.js", 36 | "index.js", 37 | "package.json", 38 | "README.md" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/cicd.yaml: -------------------------------------------------------------------------------- 1 | name: run-script-os CICD 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test ${{ matrix.os }}:${{ matrix.node-version }}/npm@${{ matrix.npm-version }} 8 | runs-on: ${{ matrix.os }} 9 | # run-script-os doesn't have dependencies. 10 | # The tests have run-script-os as a dependency 11 | # Because of that, the tests folder is the working 12 | # directory for tests 13 | defaults: 14 | run: 15 | working-directory: ./tests 16 | strategy: 17 | # Testing npm@latest using nodejs@14 on the different operating systems. 18 | # Flexible to add more test environments across the different operating systems 19 | # Include an aditional test using npm@next-7 20 | # Run all of the test environments to see all of the environments that have issues. 21 | fail-fast: false 22 | matrix: 23 | os: 24 | - macos-latest 25 | - ubuntu-latest 26 | - windows-latest 27 | node-version: [14] 28 | npm-version: [latest] 29 | include: 30 | - os: ubuntu-latest 31 | node-version: 14 32 | npm-version: next-7 33 | steps: 34 | - uses: actions/checkout@v2 35 | - name: Testing run-script-os with nodejs@${{ matrix.node-version }}/npm@${{ matrix.npm-version }} on ${{ matrix.os }} from a tests project similar to how run-script-os is used by others. 36 | uses: actions/setup-node@v2 37 | with: 38 | node-version: ${{ matrix.node-version }} 39 | - name: Update NPM to ${{ matrix.npm-version }} 40 | env: 41 | NPM_VERSION: ${{ matrix.npm-version }} 42 | run: npm install -g npm@$NPM_VERSION 43 | - name: npm version 44 | run: npm --version 45 | - name: Installing run-script-os and other testing dependencies for the tests (working-directory is ./tests) 46 | run: npm ci 47 | - name: Running tests (working-directory is ./tests) 48 | run: npm test 49 | deploy: 50 | name: Publish to NPM 51 | if: github.ref == 'refs/heads/master' 52 | runs-on: ubuntu-latest 53 | needs: test 54 | steps: 55 | - uses: actions/checkout@v2 56 | - name: Testing run-script-os with Node.js ${{ matrix.node-version }} on ${{ matrix.os }} from a tests project similar to how run-script-os is used by others. 57 | uses: actions/setup-node@v2 58 | - name: NPM Publish 59 | uses: JS-DevTools/npm-publish@v1 60 | with: 61 | token: ${{ secrets.NPM_TOKEN }} 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # run-script-os 2 | 3 | You will be able to use OS specific operations in npm scripts. 4 | 5 | ## Who would want this? 6 | If you have experienced the pain of trying to make npm scripts usable across different operating system, this package is for you! Looking at you `rm` and `del`! 7 | 8 | ## Installation 9 | `npm install --save-dev run-script-os` 10 | 11 | ## Usage 12 | 13 | Set `run-script-os` (or `run-os`) as the value of the npm script field that you want different functionality per OS. In the example below, we set `test`, but it can be any npm script. It also uses `pre` and `post` commands (explained more below). 14 | 15 | Then create OS specific scripts. In the example below, you can see: 16 | 17 | * `test:win32` 18 | * `test:linux:darwin` 19 | * `test:default` 20 | 21 | Those can have OS specific logic. 22 | 23 | `package.json` 24 | ``` 25 | { 26 | ... 27 | "scripts": { 28 | ... 29 | "test": "run-script-os", 30 | "test:win32": "echo 'del whatever you want in Windows 32/64'", 31 | "test:darwin:linux": "echo 'You can combine OS tags and rm all the things!'", 32 | "test:default": "echo 'This will run on any platform that does not have its own script'" 33 | ... 34 | }, 35 | ... 36 | } 37 | ``` 38 | 39 | **Windows Output:** 40 | ``` 41 | > npm test 42 | del whatever you want in Windows 32/64 43 | ``` 44 | 45 | **macOS and Linux Output:** 46 | ``` 47 | > npm test 48 | You can combine OS tags and rm all the things! 49 | ``` 50 | 51 | ### Aliases 52 | 53 | You can use the following aliases: 54 | 55 | * `:windows` - Alias for win32 56 | * `:macos` - Alias for darwin 57 | * `:nix` - This will run on anything considered to be a *nix OS (aix, darwin, freebsd, linux, openbsd, sunos, android) 58 | * `:default` - This will run if no platform-specific scripts are found 59 | 60 | ### Override detection settings for linux-based shells on Windows 61 | 62 | By default, run-script-os will detect cygwin/git bash as Windows. If you would rather your platform be detected as Linux under these environments: 63 | 64 | Set environment variable: 65 | 66 | ``` 67 | RUN_OS_WINBASH_IS_LINUX=true 68 | ``` 69 | 70 | ### NPM Scripts Order 71 | When you call a script like `npm test`, npm will first call `pretest` if it exists. It will then call `test`, which, if you are using `run-script-os`, it will then call `npm run test:YOUR OS`, which in turn will call `pretest:YOUR OS` before actually running `test:YOUR OS`. Then `posttest:YOUR OS` will run, and then after that `posttest` will finally execute. 72 | 73 | There is an example showing `pre` and `post` commands found in the [`package.json` of this repository](https://github.com/charlesguse/run-script-os/blob/master/package.json). 74 | 75 | OS Options: `darwin`, `freebsd`, `linux`, `sunos`, `win32` 76 | 77 | More information can be found in [Node's `process.platform`](https://nodejs.org/api/process.html#process_process_platform) and [Node's `os.platform()`](https://nodejs.org/api/os.html#os_os_platform). 78 | -------------------------------------------------------------------------------- /functions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Match script to the list of available scripts, or check for aliases 3 | * 4 | * The order for checking matches should be direct-platform and then aliases 5 | * 6 | * @param {string} script - name of the script to be matched paired to the platform or alias 7 | * @param {string} platform - name of the platform to be paired with the script 8 | * @param {array} scripts - list of available scripts defined in package.json 9 | */ 10 | exports.matchScript = function matchScript(script, platform, scripts) { 11 | /** 12 | * Save the result so we can determine if there was a match 13 | * First check for a basic match before we have to go through each script with a regex 14 | */ 15 | let result = (`${script}:${platform}` in scripts) ? `${script}:${platform}` : false; 16 | if (result) return result; 17 | 18 | /** 19 | * Regular expresion match 20 | * it helps when the "in" operator can't determine if there's a real match or not, 21 | * due to the properties changing 22 | */ 23 | let regex = new RegExp(`^(${script}):([a-zA-Z0-9-]*:)*(${platform})(:[a-zA-Z0-9-]*)*$`, "g"); 24 | for (let command in scripts) { 25 | if (command.match(regex)) return command; 26 | } 27 | 28 | /** 29 | * Alias match, allows for a more verbose description of the platform 30 | * it also helps to group similar platforms on a single execution 31 | */ 32 | switch (platform) { 33 | case 'win32': 34 | result = (`${script}:windows` in scripts) ? `${script}:windows` : false; 35 | break; 36 | 37 | case 'aix': 38 | case 'linux': 39 | case 'sunos': 40 | case 'openbsd': 41 | case 'freebsd': 42 | case 'android': 43 | result = (`${script}:nix` in scripts) ? `${script}:nix` : false; 44 | break; 45 | 46 | case 'darwin': 47 | case 'macos': 48 | /** 49 | * macOS specific scripts (e.g. brew) 50 | */ 51 | result = (`${script}:macos` in scripts) ? `${script}:macos` : false; 52 | 53 | /** 54 | * nix compatible scripts (cp, rm...) 55 | */ 56 | if (!result) result = (`${script}:nix` in scripts) ? `${script}:nix` : false; 57 | 58 | break; 59 | default: result = false; 60 | } 61 | 62 | /** 63 | * Successful finding of a given script by platform, present it. 64 | */ 65 | if (result) return result; 66 | 67 | /** 68 | * Fall to default if it's given, otherwise fail 69 | */ 70 | return (`${script}:default` in scripts) ? `${script}:default` : false; 71 | }; 72 | 73 | /** 74 | * Expand the shorthand description for npm commands 75 | * 76 | * i.e. npm i -> npm install 77 | * 78 | * @param String shorthand Shorthand command to be expanded 79 | * @return String Actual command 80 | */ 81 | exports.expandShorthand = function expandShorthand(shorthand) { 82 | switch(shorthand) { 83 | case 'i': 84 | return 'install'; 85 | 86 | case 't': 87 | case 'tst': 88 | return 'test'; 89 | 90 | /** 91 | * Expansion is not possible 92 | * @type {[type]} 93 | */ 94 | default: return shorthand; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # run-script-os tests 2 | 3 | GitHub Actions makes it trivial to test this project across the different operating systems and due to the nature of the package, it would be good to test this on the three primary OSes (Windows, Linux, Mac). 4 | 5 | --- 6 | 7 | ## Test Code Structure 8 | Tests are split up into two categories, Bash tests and PowerShell tests. The tests should be as identical as is realistic within their languages and coding styles. `windows.test.ps1` and `nix.test.bats` are about as identical as they can be while using their respective scripting languages. 9 | 10 | The Bash tests exist in `nix.test.bats` and that test file is called by `linux.test.sh` and `macos.test.sh`. The scripts set an environment variable (`NIX_OS`) to grab the proper test results (`linux/mac.expected.txt`). The environment variable `NIX_OS` DOES NOT override anything within npm-run-script itself and is only used within `nix.test.bats` for testing purposes. 11 | 12 | The PowerShell tests don't return a status code of the errors by default. This chain of PowerShell commands `Invoke-Pester -Configuration (Get-Content ./windows.configuration.json | ConvertFrom-Json) | Select-Object -ExpandProperty FailedCount | Should -BeNull"` will return a non-zero status code if there are failed tests. 13 | 14 | Broken down, `Invoke-Pester -Configuration (Get-Content ./windows.configuration.json | ConvertFrom-Json)`, Pester is pulling in a configuration file (`./windows.configuration.json`) to use to run the tests. `Invoke-Pester` returns a complex object that then needs to be queried for how many tests failed. 15 | 16 | That is handled in this next section. `| Select-Object -ExpandProperty FailedCount | Should -BeNull"` queries the object returned from Pester for the FailedCount of the tests and then the `Should` command will return a non-zero status code if `FailedCount` is anything but null. 17 | 18 | Within the NPM scripts section, all of that is wrapped in `@powershell -NoProfile -Command \"Invoke-Pester ... | Should -BeNull\""`. This is so that NPM will run the command in PowerShell regardless of which shell it was actually invoked in (often times CMD on Windows). 19 | 20 | --- 21 | 22 | ## Testing Tech/framework Used 23 | While this is an NPM package, the expected use-case is to embed this command into NPM scripts to run the proper shell commands for any operating system. For that reason, the testing frameworks are focused around bash and powershell. 24 | 25 | ### Bash Automated Testing System (Bats) 26 | Bats is a testing framework for Bash. `nix.test.bats` runs within Bats and calls Bash internally. NPM is being used to install Bats for testing only. To run these tests, cd into `run-script-os/tests` and run `npm install`. To make the bats script easy to properly interpret, you can see the shebang in the `.bats` files and that it references the local `node_modules` folder. 27 | 28 | ### Pester 29 | Pester is the built-in testing framework for Windows. Version 5+ is expected. Your Windows machine may come with an older version of Pester that expects different syntax. [This guide](https://pester-docs.netlify.app/docs/introduction/installation) will show you how to update to the newest version of Pester. 30 | 31 | --- 32 | 33 | ## Features 34 | * Tests across Windows, Linux, and Mac 35 | * Tests across multiple versions of NPM 36 | 37 | ### Wishlist (not implemented currently) 38 | * Code coverage of the NPM package during tests 39 | 40 | --- 41 | 42 | ## Installation 43 | 1. `npm install` 44 | 1. `npm test` 45 | 46 | ## Testing Framework References 47 | 1. Bats 48 | * Docs https://bats-core.readthedocs.io/en/latest/ 49 | * GitHub https://github.com/bats-core/bats-core 50 | 1. Pester Reference 51 | * Docs https://pester-docs.netlify.app/ 52 | * GitHub https://github.com/pester/pester 53 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | "use strict"; 3 | const path = require("path"); 4 | 5 | /** 6 | * Functions defined to separate the alias and regex matching 7 | */ 8 | const matchScript = require ('./functions.js').matchScript; 9 | const expandShorthand = require('./functions.js').expandShorthand; 10 | 11 | const INCORRECT_USAGE_CODE = 255 12 | const MISSING_COMMAND_CODE = 254 13 | 14 | 15 | /** 16 | * This package can only be executed from within the npm script execution context 17 | */ 18 | if (!process.env["npm_config_argv"] && !process.env["npm_lifecycle_event"]) { 19 | console.log("This is meant to be run from within npm script. See https://github.com/charlesguse/run-script-os"); 20 | process.exit(INCORRECT_USAGE_CODE); 21 | } 22 | 23 | /** 24 | * Executor process to match the script without blocking 25 | */ 26 | const spawn = require("child_process").spawn; 27 | 28 | /** 29 | * Switch to linux platform if cygwin/gitbash detected (fixes #7) 30 | * Allow overriding this behavior (fixes #11) 31 | */ 32 | let platform = process.platform; 33 | if (process.env.RUN_OS_WINBASH_IS_LINUX) { 34 | let shell = process.env.SHELL || process.env.TERM; 35 | shell = shell && shell.match("bash.exe") ? "bash.exe" : shell; 36 | platform = shell && ["bash.exe", "cygwin"].includes(shell) ? "linux" : process.platform; 37 | } 38 | 39 | /** 40 | * Scripts as found on the user's package.json 41 | */ 42 | const scripts = require(path.join(process.cwd(), "package.json")).scripts; 43 | 44 | /** 45 | * The script being executed can come from either lifecycle events or command arguments 46 | */ 47 | let options 48 | if (process.env["npm_config_argv"]) { 49 | let npmArgs = JSON.parse(process.env["npm_config_argv"]); 50 | options = npmArgs.original; 51 | } else { 52 | options = 53 | [process.env["npm_command"], process.env["npm_lifecycle_event"]]; 54 | } 55 | if (!(options[0] === "run" || options[0] === "run-script")) { 56 | options.unshift("run"); 57 | } 58 | 59 | /** 60 | * Expand shorthand command descriptors 61 | */ 62 | options[1] = expandShorthand(options[1]); 63 | 64 | // Check for yarn without install command; fixes #13 65 | const isYarn = (process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes('yarn')) ? true : false; 66 | if (isYarn && !options[1]) options[1] = 'install'; 67 | 68 | let osCommand = `${options[1]}:${platform}`; 69 | let foundMatch = true; 70 | 71 | let argument = options[1]; 72 | let event = process.env["npm_lifecycle_event"]; 73 | 74 | /** 75 | * Yarn support 76 | * Check for yarn without install command; fixes #13 77 | */ 78 | if (isYarn && !argument) { 79 | argument = 'install'; 80 | } 81 | 82 | /** 83 | * More in-depth match 84 | * Execute the regular expression to help identify missing scripts 85 | * It also tests for different aliases 86 | */ 87 | osCommand = matchScript(event || argument, platform, scripts); 88 | 89 | /** 90 | * Test again, this time to end the process gracefully 91 | */ 92 | if (!osCommand) { 93 | console.log(`run-script-os was unable to execute the script '${event || argument}'`); 94 | process.exit(MISSING_COMMAND_CODE); 95 | } 96 | 97 | /** 98 | * If it hasn't already, we set the command to be executed via npm run or npm run-script 99 | */ 100 | if (!(options[0] === "run" || options[0] === "run-script")) { 101 | options.unshift("run"); 102 | } 103 | 104 | /** 105 | * Lastly, set the script to be executed 106 | */ 107 | options[1] = osCommand; 108 | 109 | 110 | const supportedArgs = ['--no-arguments']; 111 | let args = process.argv.slice(2).map((a) => a.toLowerCase());; 112 | let argsCount = 0; 113 | for (let i = 0; i < args.length; i += 1) { 114 | if (supportedArgs.includes(args[i])) { 115 | argsCount = i; 116 | } 117 | } 118 | args = args.slice(0, argsCount); 119 | 120 | /** 121 | * Append arguments passed to the run-script-os 122 | * Check if we should be passing the original arguments to the new script 123 | * Fix for #23 124 | */ 125 | options = options.slice(0,2); 126 | if (!args.includes('--no-arguments')) { 127 | options = options.concat(process.argv.slice(2 + argsCount)); 128 | } 129 | 130 | /** 131 | * Spawn new process to run the required script 132 | * 133 | * Open either the cmd file or the cmd command, if we're in windows 134 | */ 135 | let packageManagerCommand; 136 | 137 | packageManagerCommand = isYarn ? "yarn" : "npm"; 138 | if (platform === "win32") { 139 | packageManagerCommand = packageManagerCommand + ".cmd"; 140 | } 141 | 142 | const childProcess = spawn(packageManagerCommand, options, { shell: true, stdio: "inherit"}); 143 | 144 | /** 145 | * Finish the execution 146 | */ 147 | childProcess.on("exit", (code) => { 148 | process.exit(code); 149 | }); 150 | --------------------------------------------------------------------------------