├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── main.yml │ └── publish-on-tag.yml ├── .gitignore ├── .nvmrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cli.js ├── engines ├── graaljs │ ├── extract.js │ ├── get-latest-version.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-url.js │ └── test.js ├── hermes │ ├── extract.js │ ├── get-latest-version.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-url.js │ └── test.js ├── javascriptcore │ ├── extract.js │ ├── get-latest-version.js │ ├── get-macos-name.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-url.js │ └── test.js ├── quickjs │ ├── extract.js │ ├── get-latest-version.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-url.js │ └── test.js ├── spidermonkey │ ├── extract.js │ ├── get-latest-version.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-url.js │ └── test.js ├── v8-debug │ ├── extract.js │ ├── get-latest-version.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-file-name.js │ ├── predict-url.js │ └── test.js ├── v8 │ ├── extract.js │ ├── get-latest-version.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-file-name.js │ ├── predict-url.js │ └── test.js └── xs │ ├── extract.js │ ├── get-latest-version.js │ ├── get-specific-version.js │ ├── index.js │ ├── predict-url.js │ └── test.js ├── package.json ├── screenshot.svg └── shared ├── config.js ├── download.js ├── engine.js ├── get.js ├── install-specific-version.js ├── installer.js ├── log.js ├── match-response.js ├── status.js └── unzip.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [{README.md,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | linux-main-checks: 13 | # https://github.com/actions/virtual-environments#available-environments 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | - name: Set up Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version-file: '.nvmrc' 22 | - name: Install dependencies 23 | run: npm install 24 | - name: Test 25 | run: npm run test-linux64 26 | macos: 27 | # https://github.com/actions/virtual-environments#available-environments 28 | runs-on: macos-latest 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v2 32 | - name: Install dependencies 33 | run: npm install 34 | - name: Test 35 | run: npm run test-mac64 36 | -------------------------------------------------------------------------------- /.github/workflows/publish-on-tag.yml: -------------------------------------------------------------------------------- 1 | name: publish-on-tag 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Set up Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version-file: '.nvmrc' 18 | - name: Publish 19 | env: 20 | NPM_TOKEN: ${{secrets.NPM_TOKEN}} 21 | run: | 22 | npm config set registry 'https://wombat-dressing-room.appspot.com/' 23 | npm config set '//wombat-dressing-room.appspot.com/:_authToken' '${NPM_TOKEN}' 24 | npm publish 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Installed npm modules 2 | node_modules 3 | package-lock.json 4 | 5 | # Folder view configuration files 6 | .DS_Store 7 | Desktop.ini 8 | 9 | # Thumbnail cache files 10 | ._* 11 | Thumbs.db 12 | 13 | # Files that might appear on external disks 14 | .Spotlight-V100 15 | .Trashes 16 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We’d love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. 4 | 5 | ## Contributor License Agreement 6 | 7 | Contributions to this project must be accompanied by a Contributor License Agreement. You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as part of the project. Head over to to see your current agreements on file or to sign a new one. 8 | 9 | You generally only need to submit a CLA once, so if you’ve already submitted one (even if it was for a different project), you probably don’t need to do it again. 10 | 11 | ## Code reviews 12 | 13 | All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | https://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsvu [![Build status](https://github.com/GoogleChromeLabs/jsvu/actions/workflows/main.yml/badge.svg)](https://github.com/GoogleChromeLabs/jsvu/actions/workflows/main.yml) [![jsvu on npm](https://img.shields.io/npm/v/jsvu)](https://www.npmjs.com/package/jsvu) 2 | 3 | _jsvu_ is the JavaScript (engine) Version Updater. 4 | 5 | _jsvu_ makes it easy to install recent versions of various JavaScript engines without having to compile them from source. 6 | 7 | [![](screenshot.svg)](https://asciinema.org/a/rfS1M5ynKm1hGaBqJYJj0mGCi) 8 | 9 | ## Installation 10 | 11 | **Note:** _jsvu_ requires Node.js v18+. (_jsvu_ follows the latest [active LTS](https://github.com/nodejs/Release#release-schedule) version of Node.) 12 | 13 | Install the _jsvu_ CLI: 14 | 15 | ```sh 16 | npm install jsvu -g 17 | ``` 18 | 19 | Modify your dotfiles (e.g. `~/.bashrc`) to add `~/.jsvu/bin` to your `PATH`: 20 | 21 | ```sh 22 | export PATH="${HOME}/.jsvu/bin:${PATH}" 23 | ``` 24 | 25 | Then, run `jsvu`: 26 | 27 | ```sh 28 | jsvu 29 | ``` 30 | 31 | On first run, `jsvu` prompts you for your operating system and architecture, and the list of JavaScript engines you wish to manage through `jsvu`. It then downloads and installs the latest version of each of the engines you selected. 32 | 33 | To update the installed JavaScript engines later on, just run `jsvu` again. 34 | 35 | ## Supported engines per OS 36 | 37 | | JavaScript engine | Binary name | `mac64` | `mac64arm` | `win32` | `win64` | `linux32` | `linux64` | 38 | | ------------------------- | ------------------------- | ------- | ---------- | ------- | ---------------- | --------- | --------- | 39 | | [**GraalJS**][graaljs] | `graaljs` | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | 40 | | [**Hermes**][hermes] | `hermes` & `hermes-repl` | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | 41 | | [**JavaScriptCore**][jsc] | `javascriptcore` or `jsc` | ✅ | ✅ | ❌ | ✅ \* | ❌ | ✅ | 42 | | [**QuickJS-ng**][quickjs] | `quickjs` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 43 | | [**SpiderMonkey**][sm] | `spidermonkey` or `sm` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 44 | | [**V8**][v8] | `v8` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 45 | | [**V8 debug**][v8] | `v8-debug` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 46 | | [**XS**][xs] | `xs` | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | 47 | 48 | \* JavaScriptCore requires external dependencies to run on Windows: 49 | - On 32-bit Windows, install [iTunes](https://www.apple.com/itunes/download/). 50 | - On 64-bit Windows, download the latest [`WebKitRequirements`](https://github.com/WebKitForWindows/WebKitRequirements/releases) and add its `bin64` directory to your `PATH`. 51 | 52 | [graaljs]: https://github.com/oracle/graaljs 53 | [hermes]: https://github.com/facebook/hermes/issues/17 54 | [jsc]: https://bugs.webkit.org/show_bug.cgi?id=179945 55 | [quickjs]: https://github.com/GoogleChromeLabs/jsvu/issues/141 56 | [sm]: https://bugzilla.mozilla.org/show_bug.cgi?id=1336514 57 | [v8]: https://bugs.chromium.org/p/chromium/issues/detail?id=936383 58 | [xs]: https://github.com/Moddable-OpenSource/moddable-xst 59 | 60 | ## Integration with `eshost-cli` 61 | 62 | [`eshost-cli`](https://github.com/bterlson/eshost-cli) makes it easy to run and compare code in all JavaScript engines that `jsvu` installs. 63 | 64 | First, install `eshost-cli`: 65 | 66 | ```sh 67 | npm install -g eshost-cli 68 | ``` 69 | 70 | Then, either automatically configure `jsvu`-installed hosts: 71 | 72 | ```sh 73 | eshost --configure-jsvu 74 | ``` 75 | 76 | …or tell `eshost-cli` where `jsvu` installs each JavaScript engine following the instructions below. 77 | 78 | ### Linux/Mac 79 | 80 | ```sh 81 | eshost --add 'GraalJS' graaljs ~/.jsvu/bin/graaljs 82 | eshost --add 'JavaScriptCore' jsc ~/.jsvu/bin/javascriptcore 83 | eshost --add 'QuickJS' qjs ~/.jsvu/bin/quickjs 84 | eshost --add 'SpiderMonkey' jsshell ~/.jsvu/bin/spidermonkey 85 | eshost --add 'V8 --harmony' d8 ~/.jsvu/bin/v8 --args '--harmony' 86 | eshost --add 'V8' d8 ~/.jsvu/bin/v8 87 | eshost --add 'XS' xs ~/.jsvu/bin/xs 88 | ``` 89 | 90 | ### Windows 91 | 92 | ```bat 93 | eshost --add "GraalJS" graaljs "%USERPROFILE%\.jsvu\bin\graaljs.cmd" 94 | eshost --add "JavaScriptCore" jsc "%USERPROFILE%\.jsvu\bin\javascriptcore.cmd" 95 | eshost --add "SpiderMonkey" jsshell "%USERPROFILE%\.jsvu\bin\spidermonkey.cmd" 96 | eshost --add "V8 --harmony" d8 "%USERPROFILE%\.jsvu\bin\v8.cmd" --args "--harmony" 97 | eshost --add "V8" d8 "%USERPROFILE%\.jsvu\bin\v8.cmd" 98 | eshost --add "XS" xs "%USERPROFILE%\.jsvu\bin\xs.cmd" 99 | ``` 100 | 101 | That’s it! You can now run code snippets in all those engines with a single command: 102 | 103 | ```sh 104 | eshost -e 'new RegExp("\n").toString()' # https://crbug.com/v8/1982 105 | 106 | eshost -e '(function maxCallStackSize() { try { return 1 + maxCallStackSize(); } catch (_) { return 1; }}())' 107 | 108 | eshost -e 'Date.parse("1 Octopus 2018")' 109 | ``` 110 | 111 | ## Integration with non-interactive environments 112 | 113 | On your personal devices, the only command you’ll ever need is `jsvu` as described above. There are no command-line flags to remember. 👋🏻 114 | 115 | However, there are use cases for running jsvu within non-interactive environments (e.g. as part of continuous integration), where it’s desirable to bypass the initial `jsvu` prompt asking to confirm your operating system, architecture, and the list of JavaScript engines to install. Here’s how to do that: 116 | 117 | ```sh 118 | jsvu --os=linux64 --engines=all 119 | # Equivalent to: 120 | jsvu --os=linux64 --engines=graaljs,hermes,javascriptcore,quickjs,spidermonkey,v8,xs 121 | ``` 122 | 123 | If the operating system and architecture are not known in advance, the `--os=default` flag attempts to guess the correct value from the running environment. This might not be right for example if running a 32-bit Node.js process on a 64-bit machine. 124 | 125 | Note that `--engines=all` does not install the `v8-debug` binaries. 126 | 127 | ## Installing specific versions 128 | 129 | jsvu also supports installing specific versions alongside the main engine binaries (which it keeps up to date). Here’s an example: 130 | 131 | ```sh 132 | jsvu v8@7.2.502 133 | ``` 134 | 135 | Binaries installed using this method are named `${BINARY}-${VERSION}`, so that the above example installs a binary named `v8-7.2.502`. This way, there’s never any conflict with the main `v8` binary, which jsvu can keep up to date. 136 | 137 | This feature works for all the supported engines: 138 | 139 | ```sh 140 | jsvu graaljs@20.2.0 141 | jsvu hermes@0.6.0 142 | jsvu javascriptcore@242640 143 | jsvu quickjs@2019-08-18 144 | jsvu spidermonkey@66.0b13 145 | jsvu v8@7.2.502 146 | jsvu v8-debug@7.1.302 147 | jsvu xs@8.7.0 148 | ``` 149 | 150 | If you pass in an invalid version number, or if the JavaScript engine creators don’t provide a precompiled binary for that specific version, jsvu shows an error. 151 | 152 | As a shorthand, for `v8` and `v8-debug` builds, jsvu can even figure out the last known good revision within a [milestone](https://v8.dev/docs/version-numbers). To install the latest available V8 v7.2.x for example, run: 153 | 154 | ```sh 155 | jsvu v8@7.2 156 | # jsvu figures out that this means v7.2.502, and then installs that version. 157 | ``` 158 | 159 | ## Security considerations 160 | 161 | _jsvu_ avoids the need for `sudo` privileges by installing everything in `~/.jsvu/bin` rather than, say, `/usr/bin`. 162 | 163 | _jsvu_ downloads files over HTTPS, and only uses URLs that are controlled by the creators of the JavaScript engine or, in the case of JavaScriptCore on Linux, the port maintainers. 164 | 165 | ## For maintainers 166 | 167 | ### How to publish new releases 168 | 169 | 1. On the `main` branch, bump the version number in `package.json`: 170 | 171 | ```sh 172 | npm version patch -m 'Release v%s' 173 | ``` 174 | 175 | Instead of `patch`, use `minor` or `major` [as needed](https://semver.org/). 176 | 177 | Note that this produces a Git commit + tag. 178 | 179 | 1. Push the release commit and tag: 180 | 181 | ```sh 182 | git push && git push --tags 183 | ``` 184 | 185 | Our CI then automatically publishes the new release to npm. 186 | 187 | ## Author 188 | 189 | [Mathias Bynens](https://mathiasbynens.be/) ([@mathias](https://twitter.com/mathias)) 190 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Copyright 2019 Google Inc. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the “License”); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // . 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an “AS IS” BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | 'use strict'; 17 | 18 | const os = require('os'); 19 | 20 | const inquirer = require('inquirer'); 21 | const updateNotifier = require('update-notifier'); 22 | 23 | const pkg = require('./package.json'); 24 | const log = require('./shared/log.js'); 25 | const { getStatus, setStatus } = require('./shared/status.js'); 26 | 27 | const getPlatform = () => { 28 | const platform = os.platform(); 29 | switch (platform) { 30 | case 'darwin': { 31 | return 'mac'; 32 | } 33 | case 'win32': { 34 | return 'win'; 35 | } 36 | default: { 37 | return 'linux'; 38 | } 39 | } 40 | }; 41 | 42 | const osChoices = [ 43 | { name: 'macOS 64-bit', value: 'mac64' }, 44 | { name: 'macOS M1 64-bit', value: 'mac64arm' }, 45 | { name: 'Linux 32-bit', value: 'linux32' }, 46 | { name: 'Linux 64-bit', value: 'linux64' }, 47 | { name: 'Windows 32-bit', value: 'win32' }, 48 | { name: 'Windows 64-bit', value: 'win64' }, 49 | ]; 50 | 51 | const guessOs = () => { 52 | const platform = getPlatform(); 53 | if (platform === 'mac') { 54 | if (os.arch() === 'arm64') { 55 | return 'mac64arm'; 56 | } 57 | return 'mac64'; 58 | } 59 | // Note: `os.arch()` returns the architecture of the Node.js process, 60 | // which does not necessarily correspond to the system architecture. 61 | // Still, if the user runs a 64-bit version of Node.js, it’s safe to 62 | // assume the underlying architecture is 64-bit as well. 63 | // https://github.com/nodejs/node/issues/17036 64 | const arch = os.arch().includes('64') ? '64' : '32'; 65 | const identifier = `${platform}${arch}`; 66 | return identifier; 67 | }; 68 | 69 | const promptOs = () => { 70 | return inquirer.prompt({ 71 | name: 'step', 72 | type: 'list', 73 | message: 'What is your operating system?', 74 | choices: osChoices, 75 | default: guessOs(), 76 | }); 77 | }; 78 | 79 | const engineChoices = [ 80 | { 81 | name: 'GraalJS', 82 | value: 'graaljs', 83 | checked: true, 84 | }, 85 | { 86 | name: 'Hermes', 87 | value: 'hermes', 88 | checked: true, 89 | }, 90 | { 91 | name: 'JavaScriptCore', 92 | value: 'javascriptcore', 93 | checked: true, 94 | }, 95 | { 96 | name: 'QuickJS', 97 | value: 'quickjs', 98 | checked: true, 99 | }, 100 | { 101 | name: 'SpiderMonkey', 102 | value: 'spidermonkey', 103 | checked: true, 104 | }, 105 | { 106 | name: 'V8', 107 | value: 'v8', 108 | checked: true, 109 | }, 110 | { 111 | name: 'V8 debug', 112 | value: 'v8-debug', 113 | checked: false, 114 | }, 115 | { 116 | name: 'XS', 117 | value: 'xs', 118 | checked: true, 119 | }, 120 | ]; 121 | 122 | const promptEngines = () => { 123 | return inquirer.prompt({ 124 | name: 'step', 125 | type: 'checkbox', 126 | message: 'Which JavaScript engines would you like to install?', 127 | choices: engineChoices, 128 | }); 129 | }; 130 | 131 | (async () => { 132 | 133 | log.banner(pkg.version); 134 | 135 | // Warn if an update is available. 136 | updateNotifier({ pkg }).notify(); 137 | 138 | // Read the user configuration + CLI arguments, and prompt for any 139 | // missing info. 140 | const status = getStatus(); 141 | 142 | const args = process.argv.slice(2); 143 | for (const arg of args) { 144 | if (arg.startsWith('--os=')) { 145 | const os = arg.split('=')[1]; 146 | status.os = os === 'default' ? guessOs() : os; 147 | } 148 | else if (arg.startsWith('--engines=')) { 149 | const enginesArg = arg.split('=')[1]; 150 | const engines = enginesArg === 'all' ? 151 | engineChoices.filter(choice => choice.checked).map(choice => choice.value) : 152 | enginesArg.split(','); 153 | status.engines = engines; 154 | } 155 | else if (arg.includes('@')) { 156 | const [engine, version] = arg.split('@'); 157 | status.engine = engine; 158 | status.version = version; 159 | } 160 | else { 161 | const wantsHelp = arg === '--help' || arg === '-h'; 162 | if (!wantsHelp) { 163 | console.error('\nUnrecognized argument: ' + JSON.stringify(arg) + '\n'); 164 | } 165 | console.log('[@]'); 166 | console.log(`[--os={${ osChoices.map(choice => choice.value).join(',') },default}]`); 167 | console.log(`[--engines={${ engineChoices.map(choice => choice.value).join(',') }},…]`); 168 | 169 | console.log('\nComplete documentation is online:'); 170 | console.log('https://github.com/GoogleChromeLabs/jsvu#readme'); 171 | return; 172 | } 173 | } 174 | 175 | if (status.os === undefined) { 176 | status.os = (await promptOs()).step; 177 | setStatus(status); 178 | } else { 179 | log.success(`Read OS from config: ${status.os}`); 180 | } 181 | 182 | // The user provided a specific engine + version, e.g. `jsvu v8@7.2`. 183 | if (status.engine && status.version) { 184 | const { engine, version } = status; 185 | log.success(`Read engine + version from CLI argument: ${engine} v${ 186 | version}`); 187 | const installSpecificEngineVersion = 188 | require('./shared/install-specific-version.js'); 189 | await installSpecificEngineVersion({ 190 | ...require(`./engines/${engine}/index.js`), 191 | status: status, 192 | }); 193 | return; 194 | } 195 | 196 | // The user wants to install or update engines, but we don’t know 197 | // which ones. 198 | if (status.engines === undefined || status.engines.length === 0) { 199 | status.engines = (await promptEngines()).step; 200 | if (status.engines.length === 0) { 201 | log.failure('No JavaScript engines selected. Nothing to do…'); 202 | } 203 | setStatus(status); 204 | } else { 205 | log.success(`Read engines from config: ${status.engines.join(', ')}`); 206 | } 207 | 208 | // Install the desired JavaScript engines. 209 | const updateEngine = require('./shared/engine.js'); 210 | for (const engine of status.engines) { 211 | if (engine === 'chakra') { 212 | console.warn('Chakra is no longer supported. https://github.com/GoogleChromeLabs/jsvu/issues/160'); 213 | continue; 214 | } 215 | await updateEngine({ 216 | status: status, 217 | ...require(`./engines/${engine}/index.js`), 218 | }); 219 | } 220 | 221 | })(); 222 | -------------------------------------------------------------------------------- /engines/graaljs/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | const fs = require('fs'); 18 | 19 | const tar = require('tar'); 20 | 21 | const { Installer } = require('../../shared/installer.js'); 22 | const unzip = require('../../shared/unzip.js'); 23 | 24 | const extract = ({ filePath, binary, alias, os }) => { 25 | return new Promise(async (resolve, reject) => { 26 | const tmpPath = path.dirname(filePath); 27 | if (os.startsWith('win')) { 28 | await unzip({ 29 | from: filePath, 30 | to: tmpPath, 31 | }); 32 | } else { 33 | await tar.extract({ 34 | file: filePath, 35 | cwd: tmpPath, 36 | }); 37 | } 38 | const installer = new Installer({ 39 | engine: binary, 40 | path: tmpPath, 41 | }); 42 | switch (os) { 43 | case 'mac64': 44 | case 'linux64': { 45 | const directoryName = fs.readdirSync(tmpPath).find(file => file.startsWith('graaljs')); 46 | const executableName = `${directoryName}/bin/js`; 47 | installer.installBinary(executableName, { symlink: false }); 48 | installer.installLibraryGlob(`${directoryName}/lib/*.so`); 49 | installer.installLibraryGlob(`${directoryName}/modules/*.jar`); 50 | installer.installBinarySymlink({ [executableName]: binary }); 51 | break; 52 | } 53 | case 'win64': { 54 | const directoryName = fs.readdirSync(tmpPath).find(file => file.startsWith('graaljs')); 55 | const executableName = `${directoryName}\\bin\\js.exe`; 56 | installer.installBinary( 57 | { [executableName]: `${binary}.exe` }, 58 | { symlink: false } 59 | ); 60 | installer.installScript({ 61 | name: `${binary}.cmd`, 62 | generateScript: (targetPath) => { 63 | return ` 64 | @echo off 65 | "${targetPath}\\${binary}.exe" %* 66 | `; 67 | } 68 | }); 69 | break; 70 | } 71 | } 72 | resolve(); 73 | }); 74 | }; 75 | 76 | module.exports = extract; 77 | -------------------------------------------------------------------------------- /engines/graaljs/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | 18 | const getLatestVersion = async () => { 19 | const url = 'https://api.github.com/repos/oracle/graaljs/releases/latest'; 20 | const response = await get(url, { 21 | json: true, 22 | }); 23 | const data = response.body; 24 | const version = data.tag_name.replace(/^(vm-|graal-)/, ''); // Strip prefix. 25 | return version; 26 | }; 27 | 28 | module.exports = getLatestVersion; 29 | -------------------------------------------------------------------------------- /engines/graaljs/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getSpecificVersion = (version) => { 17 | // If we ever want to add logic that maps e.g. `'0.1'` to the latest 18 | // available version in that range (e.g. `'0.1.0'`), it can go here. 19 | return version; 20 | }; 21 | 22 | module.exports = getSpecificVersion; 23 | -------------------------------------------------------------------------------- /engines/graaljs/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'GraalJS', 18 | id: 'graaljs', 19 | alias: false, 20 | }; 21 | -------------------------------------------------------------------------------- /engines/graaljs/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = (os) => { 17 | switch (os) { 18 | case 'mac64': { 19 | return 'macos'; 20 | } 21 | case 'linux64': { 22 | return 'linux'; 23 | } 24 | case 'win64': { 25 | return 'windows'; 26 | } 27 | default: { 28 | throw new Error( 29 | `GraalJS does not offer precompiled ${os} binaries.` 30 | ); 31 | } 32 | } 33 | }; 34 | 35 | const predictUrl = (version, os) => { 36 | const fileName = predictFileName(os); 37 | const ext = os.startsWith('win') ? 'zip' : 'tar.gz'; 38 | const majorVersion = parseInt(version.split('.')[0]); 39 | const prefix = majorVersion >= 23 ? 'graal-' : 'vm-'; 40 | const url = `https://github.com/oracle/graaljs/releases/download/${prefix}${version}/graaljs-${version}-${fileName}-amd64.${ext}`; 41 | return url; 42 | }; 43 | 44 | module.exports = predictUrl; 45 | -------------------------------------------------------------------------------- /engines/graaljs/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary }) => { 25 | const path = tempy.file(); 26 | const program = `print('Hi!');\n`; 27 | fs.writeFileSync(path, program); 28 | console.assert( 29 | (await execa(`${jsvuBinPath}/${binary}`, [path])).stdout === 'Hi!' 30 | ); 31 | console.assert( 32 | (await execa(`${jsvuBinPath}/${binary}`, ['-e', program])).stdout === 'Hi!' 33 | ); 34 | }; 35 | 36 | module.exports = test; 37 | -------------------------------------------------------------------------------- /engines/hermes/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | 18 | const tar = require('tar'); 19 | 20 | const { Installer } = require('../../shared/installer.js'); 21 | 22 | const extract = ({ filePath, binary, alias, os }) => { 23 | return new Promise(async (resolve, reject) => { 24 | const tmpPath = path.dirname(filePath); 25 | await tar.extract({ 26 | file: filePath, 27 | cwd: tmpPath, 28 | }); 29 | const installer = new Installer({ 30 | engine: binary, 31 | path: tmpPath, 32 | }); 33 | switch (os) { 34 | case 'mac64': 35 | case 'mac64arm': 36 | case 'linux64': { 37 | installer.installBinary({ 'hermes': binary }); 38 | installer.installBinary({ 'hermesc': `${binary}-compiler` }); 39 | break; 40 | } 41 | case 'win64': { 42 | installer.installBinary( 43 | { 'hermes.exe': `${binary}.exe` }, 44 | { symlink: false } 45 | ); 46 | installer.installBinary( 47 | { 'hermesc.exe': `${binary}-compiler.exe` }, 48 | { symlink: false } 49 | ); 50 | installer.installLibraryGlob('*.dll'); 51 | installer.installScript({ 52 | name: `${binary}.cmd`, 53 | symlink: false, 54 | generateScript: (targetPath) => { 55 | return ` 56 | @echo off 57 | "${targetPath}\\${binary}.exe" %* 58 | `; 59 | } 60 | }); 61 | installer.installScript({ 62 | name: `${binary}-compiler.cmd`, 63 | symlink: false, 64 | generateScript: (targetPath) => { 65 | return ` 66 | @echo off 67 | "${targetPath}\\${binary}-compiler.exe" %* 68 | `; 69 | } 70 | }); 71 | break; 72 | } 73 | } 74 | resolve(); 75 | }); 76 | }; 77 | 78 | module.exports = extract; 79 | -------------------------------------------------------------------------------- /engines/hermes/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | 18 | const getLatestVersion = async (os) => { 19 | const url = 'https://api.github.com/repos/facebook/hermes/releases/latest'; 20 | const response = await get(url, { 21 | json: true, 22 | }); 23 | const data = response.body; 24 | const version = data.tag_name.replace(/^v/, ''); // Strip prefix. 25 | return version; 26 | }; 27 | 28 | module.exports = getLatestVersion; 29 | -------------------------------------------------------------------------------- /engines/hermes/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getSpecificVersion = (version) => { 17 | // If we ever want to add logic that maps e.g. `'0.1'` to the latest 18 | // available version in that range (e.g. `'0.1.0'`), it can go here. 19 | return version; 20 | }; 21 | 22 | module.exports = getSpecificVersion; 23 | -------------------------------------------------------------------------------- /engines/hermes/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'Hermes', 18 | id: 'hermes', 19 | alias: false, 20 | }; 21 | -------------------------------------------------------------------------------- /engines/hermes/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = (os) => { 17 | switch (os) { 18 | case 'mac64': 19 | case 'mac64arm': { 20 | return 'darwin'; 21 | } 22 | case 'linux64': { 23 | return 'linux'; 24 | } 25 | case 'win64': { 26 | return 'windows'; 27 | } 28 | default: { 29 | throw new Error( 30 | `Hermes does not offer precompiled ${os} binaries.` 31 | ); 32 | } 33 | } 34 | }; 35 | 36 | const predictUrl = (version, os) => { 37 | const fileName = predictFileName(os); 38 | const majorVersion = parseInt(version.split('.')[0]); 39 | const minorVersion = parseInt(version.split('.')[1]); 40 | const suffix = majorVersion > 0 || minorVersion >= 13 ? '' : `-v${version}`; 41 | const url = `https://github.com/facebook/hermes/releases/download/v${version}/hermes-cli-${fileName}${suffix}.tar.gz`; 42 | return url; 43 | }; 44 | 45 | module.exports = predictUrl; 46 | -------------------------------------------------------------------------------- /engines/hermes/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary }) => { 25 | const path = tempy.file(); 26 | const program = `print('Hi!');\n`; 27 | fs.writeFileSync(path, program); 28 | console.assert( 29 | (await execa(`${jsvuBinPath}/${binary}`, [path])).stdout === 'Hi!' 30 | ); 31 | const out = (await execa(`${jsvuBinPath}/${binary}`, ['-help'])).stdout; 32 | console.assert(out.includes('Hermes driver')); 33 | // TODO: Test hermes-repl <<< 'print("Hi!");', maybe? 34 | }; 35 | 36 | module.exports = test; 37 | -------------------------------------------------------------------------------- /engines/javascriptcore/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | 18 | const { Installer } = require('../../shared/installer.js'); 19 | const unzip = require('../../shared/unzip.js'); 20 | 21 | const extract = ({ filePath, binary, alias, os }) => { 22 | return new Promise(async (resolve, reject) => { 23 | const tmpPath = path.dirname(filePath); 24 | await unzip({ 25 | from: filePath, 26 | to: tmpPath, 27 | }); 28 | switch (os) { 29 | case 'mac64': 30 | case 'mac64arm': { 31 | const installer = new Installer({ 32 | engine: binary, 33 | path: `${tmpPath}/Release`, 34 | }); 35 | installer.installLibraryGlob('JavaScriptCore.framework/*'); 36 | installer.installBinary({ 'jsc': binary }, { symlink: false }); 37 | installer.installScript({ 38 | name: binary, 39 | alias: alias, 40 | generateScript: (targetPath) => { 41 | return ` 42 | #!/usr/bin/env bash 43 | DYLD_FRAMEWORK_PATH="${targetPath}" DYLD_LIBRARY_PATH="${targetPath}" "${targetPath}/${binary}" "$@" 44 | `; 45 | } 46 | }); 47 | break; 48 | } 49 | case 'linux32': 50 | case 'linux64': { 51 | const installer = new Installer({ 52 | engine: binary, 53 | path: tmpPath, 54 | }); 55 | installer.installLibraryGlob('lib/*'); 56 | installer.installBinary({ 'bin/jsc': binary }, { symlink: false }); 57 | installer.installScript({ 58 | name: binary, 59 | alias: alias, 60 | generateScript: (targetPath) => { 61 | return ` 62 | #!/usr/bin/env bash 63 | LD_LIBRARY_PATH="${targetPath}/lib" exec "${targetPath}/lib/ld-linux${ 64 | os === 'linux64' ? '-x86-64' : '' }.so.2" "${targetPath}/${binary}" "$@" 65 | `; 66 | } 67 | }); 68 | break; 69 | } 70 | case 'win32': 71 | case 'win64': { 72 | const installer = new Installer({ 73 | engine: binary, 74 | path: `${tmpPath}/bin${os === 'win64' ? '64' : '32'}`, 75 | }); 76 | installer.installLibraryGlob('JavaScriptCore.resources/*'); 77 | installer.installLibraryGlob('*.dll'); 78 | installer.installLibraryGlob('*.pdb'); 79 | // One DLL that gets loaded by `jsc.exe`, `jscLib.dll`, depends on the 80 | // filename of `jsc.exe`. Because of that, we avoid renaming `jsc.exe` 81 | // to `javascriptcore.exe` on Windows. 82 | installer.installBinary('jsc.exe', { symlink: false }); 83 | installer.installScript({ 84 | name: `${binary}.cmd`, 85 | alias: `${alias}.cmd`, 86 | symlink: false, 87 | generateScript: (targetPath) => { 88 | return ` 89 | @echo off 90 | "${targetPath}\\jsc.exe" %* 91 | `; 92 | } 93 | }); 94 | console.log('NOTE: JavaScriptCore requires external dependencies to run on Windows. Please see README.md for details.'); 95 | break; 96 | } 97 | } 98 | resolve(); 99 | }); 100 | }; 101 | 102 | module.exports = extract; 103 | -------------------------------------------------------------------------------- /engines/javascriptcore/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | const matchResponse = require('../../shared/match-response.js'); 18 | const getMacOsName = require('./get-macos-name.js'); 19 | 20 | const hashToRevision = async (hash) => { 21 | const revision = await matchResponse({ 22 | url: `https://api.github.com/repos/WebKit/WebKit/commits/${hash}`, 23 | regex: /Canonical link: https:\/\/commits\.webkit\.org\/(\d+)@main/, 24 | }); 25 | return revision; 26 | }; 27 | 28 | const getLatestCommitHashOrRevisionFromBuilder = async (builderId) => { 29 | const url = `https://build.webkit.org/api/v2/builders/${builderId 30 | }/builds?order=-number&limit=1&property=got_revision&complete=true&results=0`; 31 | const response = await get(url, { 32 | json: true, 33 | }); 34 | const data = response.body; 35 | const hash = data.builds[0].properties.got_revision[0]; 36 | // Confusingly, `hash` is either a commit hash (seemingly for modern 37 | // builders) or a revision number (for older builders). 38 | return hash; 39 | }; 40 | 41 | const getLatestRevisionFromBuilder = async (builderId) => { 42 | const hash = await getLatestCommitHashOrRevisionFromBuilder(builderId); 43 | const revision = await hashToRevision(hash); 44 | return revision; 45 | }; 46 | 47 | const getLatestVersion = (os) => { 48 | switch (os) { 49 | case 'linux64': { 50 | return matchResponse({ 51 | // https://github.com/GoogleChromeLabs/jsvu/issues/98 52 | url: `https://webkitgtk.org/jsc-built-products/x86_64/release/LAST-IS`, 53 | regex: /(\d+)@main\.zip/, 54 | }); 55 | } 56 | case 'win64': { 57 | // Builder name: WinCairo-64-bit-WKL-Release-Build 58 | // https://build.webkit.org/#/builders/27 59 | return getLatestRevisionFromBuilder(27); 60 | } 61 | case 'mac64': 62 | case 'mac64arm': { 63 | const name = getMacOsName(); 64 | if (name === 'ventura') { 65 | // Builder name: Apple-Ventura-Release-Build 66 | // https://build.webkit.org/#/builders/706 67 | // This publishes universal x86_64 + arm64 binaries. 68 | return getLatestRevisionFromBuilder(706); 69 | } else if (name === 'monterey') { 70 | // Builder name: Apple-Monterey-Release-Build 71 | // https://build.webkit.org/#/builders/368 72 | // This publishes universal x86_64 + arm64 binaries. 73 | return getLatestRevisionFromBuilder(368); 74 | } else if (name === 'sonoma') { 75 | // Builder name: Apple-Sonoma-Release-Build 76 | // https://build.webkit.org/#/builders/938 77 | // This publishes universal x86_64 + arm64 binaries. 78 | return getLatestRevisionFromBuilder(938); 79 | } else if (name === 'sequoia') { 80 | // Builder name: Apple-Sequoia-Release-Build 81 | // https://build.webkit.org/#/builders/1223 82 | // This publishes universal x86_64 + arm64 binaries. 83 | return getLatestRevisionFromBuilder(1223); 84 | } else { 85 | throw new Error(`Unknown MacOS name: ${name}.`); 86 | } 87 | } 88 | default: { 89 | throw new Error( 90 | `JavaScriptCore does not offer precompiled ${os} binaries.` 91 | ); 92 | } 93 | } 94 | }; 95 | 96 | module.exports = getLatestVersion; 97 | -------------------------------------------------------------------------------- /engines/javascriptcore/get-macos-name.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { execSync } = require('node:child_process'); 4 | 5 | const SEARCH_PATTERN = 'SOFTWARE LICENSE AGREEMENT FOR macOS'; 6 | const LICENSE_FILE_PATH = 7 | '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf'; 8 | 9 | const getMacOsName = () => { 10 | const cmd = `awk '/${SEARCH_PATTERN}/' '${LICENSE_FILE_PATH}'`; 11 | const stdout = execSync(cmd).toString(); 12 | let name = stdout.substring(stdout.indexOf(SEARCH_PATTERN) + SEARCH_PATTERN.length); 13 | name = name.replace(/[^a-zA-Z]/g, '').toLowerCase(); 14 | return name; 15 | }; 16 | 17 | module.exports = getMacOsName; 18 | -------------------------------------------------------------------------------- /engines/javascriptcore/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getSpecificVersion = (version) => { 17 | // If we ever want to add logic that maps a given revision number 18 | // to some other number, it can go here. 19 | return version; 20 | }; 21 | 22 | module.exports = getSpecificVersion; 23 | -------------------------------------------------------------------------------- /engines/javascriptcore/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'JavaScriptCore', 18 | id: 'javascriptcore', 19 | alias: 'jsc', 20 | }; 21 | -------------------------------------------------------------------------------- /engines/javascriptcore/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getMacOsName = require('./get-macos-name.js'); 17 | 18 | const predictUrl = (version, os) => { 19 | switch (os) { 20 | case 'mac64': 21 | case 'mac64arm': { 22 | const name = getMacOsName(); 23 | return `https://s3-us-west-2.amazonaws.com/minified-archives.webkit.org/mac-${name}-x86_64%20arm64-release/${version}@main.zip`; 24 | } 25 | case 'linux64': { 26 | return `https://webkitgtk.org/jsc-built-products/x86_64/release/${version}@main.zip`; 27 | } 28 | case 'win64': { 29 | return `https://s3-us-west-2.amazonaws.com/archives.webkit.org/wincairo-x86_64-release/${version}@main.zip`; 30 | } 31 | default: { 32 | throw new Error( 33 | `JavaScriptCore does not offer precompiled ${os} binaries.` 34 | ); 35 | } 36 | } 37 | }; 38 | 39 | module.exports = predictUrl; 40 | -------------------------------------------------------------------------------- /engines/javascriptcore/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary, alias }) => { 25 | const path = tempy.file(); 26 | const program = `print('Hi!');\n`; 27 | fs.writeFileSync(path, program); 28 | console.assert( 29 | (await execa(`${jsvuBinPath}/${binary}`, [path])).stdout === 'Hi!' 30 | ); 31 | console.assert( 32 | (await execa(`${jsvuBinPath}/${alias}`, ['-e', program])).stdout === 'Hi!' 33 | ); 34 | }; 35 | 36 | module.exports = test; 37 | -------------------------------------------------------------------------------- /engines/quickjs/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | 18 | const { Installer } = require('../../shared/installer.js'); 19 | 20 | const extract = ({ filePath, binary, os }) => { 21 | return new Promise(async (resolve, reject) => { 22 | const tmpPath = path.dirname(filePath); 23 | const bin = path.basename(filePath); 24 | const installer = new Installer({ 25 | engine: binary, 26 | path: tmpPath, 27 | }); 28 | switch (os) { 29 | case 'win32': 30 | case 'win64': { 31 | installer.installBinary( 32 | { [`${bin}`]: 'qjs.exe' }, 33 | { symlink: false } 34 | ); 35 | installer.installScript({ 36 | name: `${binary}.cmd`, 37 | generateScript: (targetPath) => { 38 | return ` 39 | @echo off 40 | "${targetPath}\\qjs.exe" %* 41 | `; 42 | } 43 | }); 44 | break; 45 | } 46 | case 'mac64': 47 | case 'mac64arm': 48 | case 'linux32': 49 | case 'linux64': { 50 | installer.installBinary({ [`${bin}`]: 'qjs' }); 51 | installer.installScript({ 52 | name: binary, 53 | generateScript: (targetPath) => { 54 | return ` 55 | #!/usr/bin/env bash 56 | "${targetPath}/qjs" "$@" 57 | `; 58 | } 59 | }); 60 | break; 61 | } 62 | } 63 | resolve(); 64 | }); 65 | }; 66 | 67 | module.exports = extract; 68 | -------------------------------------------------------------------------------- /engines/quickjs/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | 18 | const getLatestVersion = async () => { 19 | const url = 'https://api.github.com/repos/quickjs-ng/quickjs/releases/latest'; 20 | const response = await get(url, { 21 | json: true, 22 | }); 23 | const data = response.body; 24 | const version = data.tag_name.replace(/^v/, ''); 25 | return version; 26 | }; 27 | 28 | module.exports = getLatestVersion; 29 | -------------------------------------------------------------------------------- /engines/quickjs/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getSpecificVersion = (version) => { 17 | // If we ever want to add logic that maps e.g. `'2019-08'` to the 18 | // latest available version in that range (e.g. `'2019-08-18'`), it 19 | // can go here. 20 | return version; 21 | }; 22 | 23 | module.exports = getSpecificVersion; 24 | -------------------------------------------------------------------------------- /engines/quickjs/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'QuickJS', 18 | id: 'quickjs', 19 | alias: false, 20 | }; 21 | -------------------------------------------------------------------------------- /engines/quickjs/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = (os) => { 17 | switch (os) { 18 | case 'win32': { 19 | return 'windows-x86.exe'; 20 | } 21 | case 'win64': { 22 | return 'windows-x86_64.exe'; 23 | } 24 | case 'linux32': { 25 | return 'linux-x86'; 26 | } 27 | case 'linux64': { 28 | return 'linux-x86_64'; 29 | } 30 | case 'mac64': 31 | case 'mac64arm': { 32 | return 'darwin'; 33 | } 34 | default: { 35 | throw new Error( 36 | `QuickJS does not offer precompiled ${os} binaries.` 37 | ); 38 | } 39 | } 40 | }; 41 | 42 | const predictUrl = (version, os) => { 43 | const fileName = predictFileName(os); 44 | const url = `https://github.com/quickjs-ng/quickjs/releases/download/v${version}/qjs-${fileName}`; 45 | return url; 46 | }; 47 | 48 | module.exports = predictUrl; 49 | -------------------------------------------------------------------------------- /engines/quickjs/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary }) => { 25 | const path = tempy.file(); 26 | const program = `print('Hi!');\n`; 27 | fs.writeFileSync(path, program); 28 | console.assert( 29 | (await execa(`${jsvuBinPath}/${binary}`, [path])).stdout === 'Hi!' 30 | ); 31 | console.assert( 32 | (await execa(`${jsvuBinPath}/${binary}`, ['-e', program])).stdout === 'Hi!' 33 | ); 34 | }; 35 | 36 | module.exports = test; 37 | -------------------------------------------------------------------------------- /engines/spidermonkey/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | 18 | const { Installer } = require('../../shared/installer.js'); 19 | const unzip = require('../../shared/unzip.js'); 20 | 21 | const extract = ({ filePath, binary, alias, os }) => { 22 | return new Promise(async (resolve, reject) => { 23 | const tmpPath = path.dirname(filePath); 24 | await unzip({ 25 | from: filePath, 26 | to: tmpPath, 27 | }); 28 | const installer = new Installer({ 29 | engine: binary, 30 | path: tmpPath, 31 | }); 32 | switch (os) { 33 | case 'mac64': 34 | case 'mac64arm': { 35 | installer.installLibraryGlob('*.dylib'); 36 | installer.installBinary({ 'js': binary }); 37 | installer.installBinarySymlink({ [binary]: alias }); 38 | break; 39 | } 40 | case 'linux32': 41 | case 'linux64': { 42 | installer.installLibraryGlob('*.so'); 43 | installer.installBinary({ 'js': binary }, { symlink: false }); 44 | installer.installScript({ 45 | name: binary, 46 | alias: alias, 47 | generateScript: (targetPath) => { 48 | return ` 49 | #!/usr/bin/env bash 50 | LD_LIBRARY_PATH="${targetPath}" "${targetPath}/${binary}" "$@" 51 | `; 52 | } 53 | }); 54 | break; 55 | } 56 | case 'win32': 57 | case 'win64': { 58 | installer.installLibraryGlob('*.dll'); 59 | installer.installBinary( 60 | { 'js.exe': `${binary}.exe` }, 61 | { symlink: false } 62 | ); 63 | installer.installScript({ 64 | name: `${binary}.cmd`, 65 | alias: `${alias}.cmd`, 66 | symlink: false, 67 | generateScript: (targetPath) => { 68 | return ` 69 | @echo off 70 | "${targetPath}\\${binary}.exe" %* 71 | `; 72 | } 73 | }); 74 | break; 75 | } 76 | } 77 | resolve(); 78 | }); 79 | }; 80 | 81 | module.exports = extract; 82 | -------------------------------------------------------------------------------- /engines/spidermonkey/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | 18 | const getLatestVersion = () => { 19 | // Note: It’d be nice to just use 20 | // https://product-details.mozilla.org/1.0/firefox_versions.json 21 | // but it doesn’t seem to be as up-to-date. At the time of writing 22 | // it lists v58.0b4 instead of v58.0b5, whereas the resource below 23 | // does include v58.0b5. 🤷🏼‍♂️ 24 | const url = 'https://product-details.mozilla.org/1.0/firefox_history_development_releases.json'; 25 | return new Promise(async (resolve, reject) => { 26 | try { 27 | const response = await get(url, { 28 | json: true 29 | }); 30 | const data = response.body; 31 | // Don’t rely on the upstream sort order. 32 | // https://github.com/GoogleChromeLabs/jsvu/issues/65 33 | let latestVersion = 0; 34 | let latestTimestamp = 0; 35 | for (const [key, value] of Object.entries(data)) { 36 | const timestamp = +new Date(value); 37 | if (latestTimestamp < timestamp) { 38 | latestTimestamp = timestamp; 39 | latestVersion = key; 40 | } 41 | } 42 | const version = latestVersion; 43 | resolve(version); 44 | } catch (error) { 45 | reject(error.response.body); 46 | } 47 | }); 48 | }; 49 | 50 | module.exports = getLatestVersion; 51 | -------------------------------------------------------------------------------- /engines/spidermonkey/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getSpecificVersion = (version) => { 17 | // If we ever want to add logic that maps e.g. `'66'` to the latest 18 | // available version in that range (e.g. `'66.0b13'`), it can go here. 19 | return version; 20 | }; 21 | 22 | module.exports = getSpecificVersion; 23 | -------------------------------------------------------------------------------- /engines/spidermonkey/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'SpiderMonkey', 18 | id: 'spidermonkey', 19 | alias: 'sm', 20 | }; 21 | -------------------------------------------------------------------------------- /engines/spidermonkey/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = (os) => { 17 | switch (os) { 18 | case 'mac64': 19 | case 'mac64arm': { 20 | return 'mac'; 21 | } 22 | case 'linux32': { 23 | return 'linux-i686'; 24 | } 25 | case 'linux64': { 26 | return 'linux-x86_64'; 27 | } 28 | case 'win32': { 29 | return 'win32'; 30 | } 31 | case 'win64': { 32 | return 'win64'; 33 | } 34 | default: { 35 | throw new Error( 36 | `SpiderMonkey does not offer precompiled ${os} binaries.` 37 | ); 38 | } 39 | } 40 | }; 41 | 42 | const predictUrl = (version, os) => { 43 | const fileName = predictFileName(os); 44 | const url = `https://archive.mozilla.org/pub/firefox/releases/${ 45 | version}/jsshell/jsshell-${fileName}.zip`; 46 | return url; 47 | }; 48 | 49 | module.exports = predictUrl; 50 | -------------------------------------------------------------------------------- /engines/spidermonkey/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary, alias }) => { 25 | const path = tempy.file(); 26 | fs.writeFileSync(path, `print('Hi!');\n`); 27 | console.assert( 28 | (await execa(`${jsvuBinPath}/${binary}`, [path])).stdout === 'Hi!' 29 | ); 30 | console.assert( 31 | (await execa(`${jsvuBinPath}/${alias}`, [path])).stdout === 'Hi!' 32 | ); 33 | }; 34 | 35 | module.exports = test; 36 | -------------------------------------------------------------------------------- /engines/v8-debug/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | 18 | const { Installer } = require('../../shared/installer.js'); 19 | const unzip = require('../../shared/unzip.js'); 20 | 21 | const extract = ({ filePath, binary, os }) => { 22 | return new Promise(async (resolve, reject) => { 23 | const tmpPath = path.dirname(filePath); 24 | await unzip({ 25 | from: filePath, 26 | to: tmpPath, 27 | }); 28 | const installer = new Installer({ 29 | engine: binary, 30 | path: tmpPath, 31 | }); 32 | installer.installLibrary('icudtl.dat'); 33 | const hasNativesBlob = installer.installLibrary('natives_blob.bin'); 34 | installer.installLibrary('snapshot_blob.bin'); 35 | switch (os) { 36 | case 'mac64': 37 | case 'mac64arm': { 38 | installer.installLibraryGlob('*.dylib'); 39 | installer.installBinary({ 'd8': binary }, { symlink: false }); 40 | installer.installScript({ 41 | name: binary, 42 | generateScript: (targetPath) => { 43 | const nativesBlobArg = hasNativesBlob ? ` --natives_blob="${targetPath}/natives_blob.bin"` : ''; 44 | return ` 45 | #!/usr/bin/env bash 46 | "${targetPath}/${binary}"${nativesBlobArg} --snapshot_blob="${targetPath}/snapshot_blob.bin" "$@" 47 | `; 48 | } 49 | }); 50 | break; 51 | } 52 | case 'linux32': 53 | case 'linux64': { 54 | installer.installLibraryGlob('*.so'); 55 | installer.installBinary({ 'd8': binary }, { symlink: false }); 56 | installer.installScript({ 57 | name: binary, 58 | generateScript: (targetPath) => { 59 | const nativesBlobArg = hasNativesBlob ? ` --natives_blob="${targetPath}/natives_blob.bin"` : ''; 60 | return ` 61 | #!/usr/bin/env bash 62 | "${targetPath}/${binary}"${nativesBlobArg} --snapshot_blob="${targetPath}/snapshot_blob.bin" "$@" 63 | `; 64 | } 65 | }); 66 | break; 67 | } 68 | case 'win32': 69 | case 'win64': { 70 | installer.installLibraryGlob('*.dll'); 71 | installer.installBinary( 72 | { 'd8.exe': `${binary}.exe` }, 73 | { symlink: false } 74 | ); 75 | installer.installScript({ 76 | name: `${binary}.cmd`, 77 | generateScript: (targetPath) => { 78 | const nativesBlobArg = hasNativesBlob ? ` --natives_blob="${targetPath}\\natives_blob.bin"` : ''; 79 | return ` 80 | @echo off 81 | "${targetPath}\\${binary}.exe"${nativesBlobArg} --snapshot_blob="${targetPath}\\snapshot_blob.bin" %* 82 | `; 83 | } 84 | }); 85 | break; 86 | } 87 | } 88 | resolve(); 89 | }); 90 | }; 91 | 92 | module.exports = extract; 93 | -------------------------------------------------------------------------------- /engines/v8-debug/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | const predictFileName = require('./predict-file-name.js'); 18 | 19 | const getLatestVersion = (os) => { 20 | const fileName = predictFileName(os); 21 | const url = `https://storage.googleapis.com/chromium-v8/official/canary/v8-${ 22 | fileName}-dbg-latest.json`; 23 | return new Promise(async (resolve, reject) => { 24 | try { 25 | const response = await get(url, { 26 | json: true, 27 | }); 28 | const data = response.body; 29 | const version = data.version; 30 | resolve(version); 31 | } catch (error) { 32 | reject(error); 33 | } 34 | }); 35 | }; 36 | 37 | module.exports = getLatestVersion; 38 | -------------------------------------------------------------------------------- /engines/v8-debug/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = require('../v8/get-specific-version.js'); 17 | -------------------------------------------------------------------------------- /engines/v8-debug/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'V8 debug', 18 | id: 'v8-debug', 19 | alias: false, 20 | }; 21 | -------------------------------------------------------------------------------- /engines/v8-debug/predict-file-name.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = (os) => { 17 | switch (os) { 18 | case 'mac64': { 19 | return 'mac64'; 20 | } 21 | case 'mac64arm': { 22 | return 'mac-arm64'; 23 | } 24 | case 'linux32': { 25 | return 'linux32'; 26 | } 27 | case 'linux64': { 28 | return 'linux64'; 29 | } 30 | case 'win32': { 31 | return 'win32'; 32 | } 33 | case 'win64': { 34 | return 'win64'; 35 | } 36 | default: { 37 | throw new Error( 38 | `V8 does not offer precompiled ${os} debug binaries.` 39 | ); 40 | } 41 | } 42 | }; 43 | 44 | module.exports = predictFileName; 45 | -------------------------------------------------------------------------------- /engines/v8-debug/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = require('./predict-file-name.js'); 17 | 18 | const predictUrl = (version, os) => { 19 | const fileName = predictFileName(os); 20 | const url = 21 | `https://storage.googleapis.com/chromium-v8/official/canary/v8-${ 22 | fileName}-dbg-${version}.zip`; 23 | return url; 24 | }; 25 | 26 | module.exports = predictUrl; 27 | -------------------------------------------------------------------------------- /engines/v8-debug/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary, alias }) => { 25 | const path = tempy.file(); 26 | const program = `print('Hi!');\n`; 27 | fs.writeFileSync(path, program); 28 | console.assert( 29 | (await execa(`${jsvuBinPath}/${binary}`, [path])).stdout === 'Hi!' 30 | ); 31 | console.assert( 32 | (await execa(`${jsvuBinPath}/${binary}`, ['-e', program])).stdout === 'Hi!' 33 | ); 34 | }; 35 | 36 | module.exports = test; 37 | -------------------------------------------------------------------------------- /engines/v8/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | 18 | const { Installer } = require('../../shared/installer.js'); 19 | const unzip = require('../../shared/unzip.js'); 20 | 21 | const extract = ({ filePath, binary, os }) => { 22 | return new Promise(async (resolve, reject) => { 23 | const tmpPath = path.dirname(filePath); 24 | await unzip({ 25 | from: filePath, 26 | to: tmpPath, 27 | }); 28 | const installer = new Installer({ 29 | engine: binary, 30 | path: tmpPath, 31 | }); 32 | installer.installLibrary('icudtl.dat'); 33 | const hasNativesBlob = installer.installLibrary('natives_blob.bin'); 34 | installer.installLibrary('snapshot_blob.bin'); 35 | if (os.startsWith('win')) { 36 | installer.installBinary( 37 | { 'd8.exe': `${binary}.exe` }, 38 | { symlink: false } 39 | ); 40 | installer.installScript({ 41 | name: `${binary}.cmd`, 42 | generateScript: (targetPath) => { 43 | const nativesBlobArg = hasNativesBlob ? ` --natives_blob="${targetPath}\\natives_blob.bin"` : ''; 44 | return ` 45 | @echo off 46 | "${targetPath}\\${binary}.exe"${nativesBlobArg} --snapshot_blob="${targetPath}\\snapshot_blob.bin" %* 47 | `; 48 | } 49 | }); 50 | } else { 51 | installer.installBinary({ 'd8': binary }, { symlink: false }); 52 | installer.installScript({ 53 | name: binary, 54 | generateScript: (targetPath) => { 55 | const nativesBlobArg = hasNativesBlob ? ` --natives_blob="${targetPath}/natives_blob.bin"` : ''; 56 | return ` 57 | #!/usr/bin/env bash 58 | "${targetPath}/${binary}"${nativesBlobArg} --snapshot_blob="${targetPath}/snapshot_blob.bin" "$@" 59 | `; 60 | } 61 | }); 62 | } 63 | resolve(); 64 | }); 65 | }; 66 | 67 | module.exports = extract; 68 | -------------------------------------------------------------------------------- /engines/v8/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | const predictFileName = require('./predict-file-name.js'); 18 | 19 | const getLatestVersion = (os) => { 20 | const fileName = predictFileName(os); 21 | const url = `https://storage.googleapis.com/chromium-v8/official/canary/v8-${ 22 | fileName}-rel-latest.json`; 23 | return new Promise(async (resolve, reject) => { 24 | try { 25 | const response = await get(url, { 26 | json: true, 27 | }); 28 | const data = response.body; 29 | const version = data.version; 30 | resolve(version); 31 | } catch (error) { 32 | reject(error); 33 | } 34 | }); 35 | }; 36 | 37 | module.exports = getLatestVersion; 38 | -------------------------------------------------------------------------------- /engines/v8/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getSpecificVersion = (version) => { 17 | const [major, minor, build, patch] = version.split('.'); 18 | if (build) { 19 | // Assume exact version. 20 | return version; 21 | } 22 | return new Promise(async (resolve, reject) => { 23 | try { 24 | const matchResponse = require('../../shared/match-response.js'); 25 | const build = await matchResponse({ 26 | url: `https://raw.githubusercontent.com/v8/v8/${ 27 | major}.${minor}-lkgr/include/v8-version.h`, 28 | regex: /#define V8_BUILD_NUMBER (\d+)/, 29 | }); 30 | const version = `${major}.${minor}.${build}`; 31 | resolve(version); 32 | } catch (error) { 33 | reject(error); 34 | } 35 | }); 36 | }; 37 | 38 | module.exports = getSpecificVersion; 39 | -------------------------------------------------------------------------------- /engines/v8/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'V8', 18 | id: 'v8', 19 | alias: false, 20 | }; 21 | -------------------------------------------------------------------------------- /engines/v8/predict-file-name.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = (os) => { 17 | switch (os) { 18 | case 'mac64': { 19 | return 'mac64'; 20 | } 21 | case 'mac64arm': { 22 | return 'mac-arm64'; 23 | } 24 | case 'linux32': { 25 | return 'linux32'; 26 | } 27 | case 'linux64': { 28 | return 'linux64'; 29 | } 30 | case 'win32': { 31 | return 'win32'; 32 | } 33 | case 'win64': { 34 | return 'win64'; 35 | } 36 | default: { 37 | throw new Error( 38 | `V8 does not offer precompiled ${os} binaries.` 39 | ); 40 | } 41 | } 42 | }; 43 | 44 | module.exports = predictFileName; 45 | -------------------------------------------------------------------------------- /engines/v8/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = require('./predict-file-name.js'); 17 | 18 | const predictUrl = (version, os) => { 19 | const fileName = predictFileName(os); 20 | const url = 21 | `https://storage.googleapis.com/chromium-v8/official/canary/v8-${ 22 | fileName}-rel-${version}.zip`; 23 | return url; 24 | }; 25 | 26 | module.exports = predictUrl; 27 | -------------------------------------------------------------------------------- /engines/v8/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary }) => { 25 | const path = tempy.file(); 26 | const program = `print('Hi!');\n`; 27 | fs.writeFileSync(path, program); 28 | console.assert( 29 | (await execa(`${jsvuBinPath}/${binary}`, [path])).stdout === 'Hi!' 30 | ); 31 | console.assert( 32 | (await execa(`${jsvuBinPath}/${binary}`, ['-e', program])).stdout === 'Hi!' 33 | ); 34 | }; 35 | 36 | module.exports = test; 37 | -------------------------------------------------------------------------------- /engines/xs/extract.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const path = require('path'); 17 | 18 | const { Installer } = require('../../shared/installer.js'); 19 | const unzip = require('../../shared/unzip.js'); 20 | 21 | const extract = ({ filePath, binary, os }) => { 22 | return new Promise(async (resolve, reject) => { 23 | const tmpPath = path.dirname(filePath); 24 | await unzip({ 25 | from: filePath, 26 | to: tmpPath, 27 | }); 28 | const installer = new Installer({ 29 | engine: binary, 30 | path: tmpPath, 31 | }); 32 | if (os.startsWith('win')) { 33 | installer.installBinary( 34 | { 'xst.exe': `${binary}.exe` }, 35 | { symlink: false } 36 | ); 37 | installer.installScript({ 38 | name: `${binary}.cmd`, 39 | generateScript: (targetPath) => { 40 | return ` 41 | @echo off 42 | "${targetPath}\\${binary}.exe" %* 43 | `; 44 | } 45 | }); 46 | } else { 47 | installer.installBinary({ 'xst': binary }, { symlink: true }); 48 | } 49 | resolve(); 50 | }); 51 | }; 52 | 53 | module.exports = extract; 54 | -------------------------------------------------------------------------------- /engines/xs/get-latest-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('../../shared/get.js'); 17 | 18 | const getLatestVersion = async () => { 19 | const url = 'https://api.github.com/repos/Moddable-OpenSource/moddable/releases/latest'; 20 | const response = await get(url, { 21 | json: true, 22 | }); 23 | const data = response.body; 24 | const version = data.tag_name; 25 | return version; 26 | }; 27 | 28 | module.exports = getLatestVersion; 29 | -------------------------------------------------------------------------------- /engines/xs/get-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const getSpecificVersion = (version) => { 17 | // If we ever want to add logic that maps e.g. `'8.7'` to the latest 18 | // available version in that range (e.g. `'8.7.0'`), it can go here. 19 | return version; 20 | }; 21 | 22 | module.exports = getSpecificVersion; 23 | -------------------------------------------------------------------------------- /engines/xs/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | module.exports = { 17 | name: 'XS', 18 | id: 'xs', 19 | alias: false, 20 | }; 21 | -------------------------------------------------------------------------------- /engines/xs/predict-url.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const predictFileName = (os) => { 17 | switch (os) { 18 | case 'mac64': { 19 | return 'mac64'; 20 | } 21 | case 'mac64arm':{ 22 | return 'mac64arm'; 23 | } 24 | case 'linux32': { 25 | return 'lin32'; 26 | } 27 | case 'linux64': { 28 | return 'lin64'; 29 | } 30 | case 'win64': { 31 | return 'win64'; 32 | } 33 | default: { 34 | throw new Error( 35 | `XS does not offer precompiled ${os} binaries.` 36 | ); 37 | } 38 | } 39 | }; 40 | 41 | const predictUrl = (version, os) => { 42 | const fileName = predictFileName(os); 43 | const url = `https://github.com/Moddable-OpenSource/moddable/releases/download/${version}/xst-${fileName}.zip`; 44 | return url; 45 | }; 46 | 47 | module.exports = predictUrl; 48 | -------------------------------------------------------------------------------- /engines/xs/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const execa = require('execa'); 19 | const tempy = require('tempy'); 20 | 21 | const config = require('../../shared/config.js'); 22 | const jsvuBinPath = config.binPath; 23 | 24 | const test = async ({ binary }) => { 25 | const path = tempy.file(); 26 | const program = `print('Hi!');\n`; 27 | fs.writeFileSync(path, program); 28 | console.assert( 29 | (await execa(`${jsvuBinPath}/${binary}`, ['-s', path])).stdout === 'Hi!' 30 | ); 31 | console.assert( 32 | (await execa(`${jsvuBinPath}/${binary}`, ['-e', program])).stdout === 'Hi!' 33 | ); 34 | }; 35 | 36 | module.exports = test; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsvu", 3 | "version": "3.0.0", 4 | "description": "JavaScript (engine) Version Updater", 5 | "homepage": "https://github.com/GoogleChromeLabs/jsvu", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/GoogleChromeLabs/jsvu.git" 9 | }, 10 | "bugs": "https://github.com/GoogleChromeLabs/jsvu/issues", 11 | "main": "cli.js", 12 | "bin": "cli.js", 13 | "files": [ 14 | "engines/**", 15 | "shared/**", 16 | "cli.js", 17 | "LICENSE", 18 | "README.md" 19 | ], 20 | "engines": { 21 | "node": ">=18.0.0" 22 | }, 23 | "scripts": { 24 | "test": "echo 'Use `npm run test-$os` where `$os` reflects your system.'; echo 'See `npm run` for the full list.' && exit 1", 25 | "test-mac64": "npm link; jsvu --os=mac64 --engines=all && echo '{\"os\":\"mac64\",\"engines\":[\"hermes\",\"javascriptcore\",\"spidermonkey\",\"v8\"],\"javascriptcore\":\"0\",\"spidermonkey\":\"0\",\"v8\":\"0\"}' > ~/.jsvu/status.json && jsvu", 26 | "test-mac64arm": "npm link; jsvu --os=mac64arm --engines=all && echo '{\"os\":\"mac64arm\",\"engines\":[\"hermes\",\"javascriptcore\",\"spidermonkey\",\"v8\"],\"javascriptcore\":\"0\",\"spidermonkey\":\"0\",\"v8\":\"0\"}' > ~/.jsvu/status.json && jsvu", 27 | "test-linux32": "npm link; jsvu --os=linux32 --engines=all && echo '{\"os\":\"linux32\",\"engines\":[\"javascriptcore\",\"spidermonkey\",\"v8\"],\"javascriptcore\":\"0\",\"spidermonkey\":\"0\",\"v8\":\"0\"}' > ~/.jsvu/status.json && jsvu", 28 | "test-linux64": "npm link; jsvu --os=linux64 --engines=all && echo '{\"os\":\"linux64\",\"engines\":[\"hermes\",\"javascriptcore\",\"quickjs\",\"spidermonkey\",\"v8\"],\"javascriptcore\":\"0\",\"spidermonkey\":\"0\",\"v8\":\"0\"}' > ~/.jsvu/status.json && jsvu", 29 | "test-win32": "npm link; jsvu --os=win32 --engines=all && echo '{\"os\":\"win32\",\"engines\":[\"javascriptcore\",\"spidermonkey\",\"v8\"],\"javascriptcore\":\"0\",\"spidermonkey\":\"0\",\"v8\":\"0\"}' > ~/.jsvu/status.json && jsvu", 30 | "test-win64": "npm link; jsvu --os=win64 --engines=all && jsvu && echo '{\"os\":\"win64\",\"engines\":[\"hermes\",\"javascriptcore\",\"spidermonkey\",\"v8\"],\"javascriptcore\":\"0\",\"spidermonkey\":\"0\",\"v8\":\"0\"}' > ~/.jsvu/status.json && jsvu" 31 | }, 32 | "author": { 33 | "name": "Mathias Bynens", 34 | "url": "https://mathiasbynens.be/" 35 | }, 36 | "license": "Apache-2.0", 37 | "keywords": [ 38 | "javascript engines", 39 | "installer", 40 | "javascriptcore", 41 | "jsc", 42 | "spidermonkey", 43 | "v8" 44 | ], 45 | "dependencies": { 46 | "chalk": "^2.4.2", 47 | "execa": "^2.0.4", 48 | "extract-zip": "^1.6.7", 49 | "fs-extra": "^8.1.0", 50 | "glob": "^7.1.4", 51 | "got": "^9.6.0", 52 | "inquirer": "^6.5.1", 53 | "mkdirp": "^0.5.1", 54 | "progress": "^2.0.3", 55 | "tar": "^4.4.10", 56 | "tempy": "^0.5.0", 57 | "tildify": "^2.0.0", 58 | "untildify": "^4.0.0", 59 | "update-notifier": "^3.0.1" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /screenshot.svg: -------------------------------------------------------------------------------- 1 | Downloadandchecksumverificationcompleted.Extracting…Extractioncompleted.Testingcompleted.mathiasatmathBookin/jsvu-demo$jsvu📦jsvuv1.0.0theJavaScriptengineVersionUpdater📦?Whatisyouroperatingsystem?macOS64-bit?WhichJavaScriptengineswouldyouliketoinstall?Chakra/ChakraCore,JavaScriptCore,SpiderMonkey,V8FoundlatestChakraversion:v1.7.4.URL:https://aka.ms/chakracore/cc_osx_x64_1_7_4SHA-256checksum:71473323193c6285f268160e2ec5bf8e14d6b6bc8c4844040b7b408d750b8a2aInstallinglibraryto~/.jsvu/engines/chakra/lib/libChakraCore.dylib…Installingbinaryto~/.jsvu/engines/chakra/chakra…Installingsymlinkat~/.jsvu/chakrapointingto~/.jsvu/engines/chakra/chakra…Installingsymlinkat~/.jsvu/chpointingto~/.jsvu/engines/chakra/chakra…Installinglicenseto~/.jsvu/engines/chakra/LICENSE-chakra…Chakrav1.7.4hasbeeninstalled!🎉FoundlatestJavaScriptCoreversion:v225521.URL:https://s3-us-west-2.amazonaws.com/minified-archives.webkit.org/mac-highsierra-x86_64-release/225521.zipDownloadcompleted.Installinglibraryto~/.jsvu/engines/javascriptcore/JavaScriptCore.framework/Headers…Installinglibraryto~/.jsvu/engines/javascriptcore/JavaScriptCore.framework/JavaScriptCore…Installinglibraryto~/.jsvu/engines/javascriptcore/JavaScriptCore.framework/PrivateHeaders…Installinglibraryto~/.jsvu/engines/javascriptcore/JavaScriptCore.framework/Resources…Installinglibraryto~/.jsvu/engines/javascriptcore/JavaScriptCore.framework/Versions…Installingbinaryto~/.jsvu/engines/javascriptcore/javascriptcore…Installingwrapperscriptto~/.jsvu/javascriptcore…Installingsymlinkat~/.jsvu/jscpointingto~/.jsvu/javascriptcore…JavaScriptCorev225521hasbeeninstalled!🎉FoundlatestSpiderMonkeyversion:v58.0b9.URL:https://archive.mozilla.org/pub/firefox/releases/58.0b9/jsshell/jsshell-mac.zipSHA-256checksum:af9ccc8e278a4350a254a9328b183ed14f539b975c06e647836d35a29173894dInstallinglibraryto~/.jsvu/engines/spidermonkey/libmozglue.dylib… -------------------------------------------------------------------------------- /shared/config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const untildify = require('untildify'); 17 | 18 | module.exports = { 19 | path: untildify('~/.jsvu'), 20 | binPath: untildify('~/.jsvu/bin'), 21 | os: 'mac', 22 | }; 23 | -------------------------------------------------------------------------------- /shared/download.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const ProgressBar = require('progress'); 19 | const tempy = require('tempy'); 20 | 21 | const get = require('./get.js'); 22 | 23 | const download = (url) => { 24 | return new Promise(async (resolve, reject) => { 25 | try { 26 | const bar = new ProgressBar(' [:bar] :percent', { 27 | complete: '=', 28 | incomplete: ' ', 29 | width: 72, 30 | total: 100, 31 | }); 32 | const response = await get(url, { 33 | // Download as binary file. 34 | encoding: null, 35 | }).on('downloadProgress', (progress) => { 36 | bar.update(progress.percent); 37 | }).on('error', (error) => { 38 | reject(`Download error: ${error}`); 39 | }); 40 | // Clear the progress bar. 41 | console.log('\x1B[1A\x1B[2K\x1B[1A'); 42 | const buffer = response.body; 43 | // Passing in `name` ensures that `tempy` creates a temporary directory 44 | // in which the file is created. Thus, we can later extract the archive 45 | // within this same directory and use wildcards to move its contents, 46 | // knowing that there are no other files in the directory. 47 | const filePath = tempy.file({ 48 | name: 'jsvutmpf', 49 | }); 50 | fs.writeFileSync(filePath, buffer); 51 | resolve(filePath); 52 | } catch (error) { 53 | reject(error); 54 | } 55 | }); 56 | }; 57 | 58 | module.exports = download; 59 | -------------------------------------------------------------------------------- /shared/engine.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const { setStatus } = require('../shared/status.js'); 17 | const log = require('../shared/log.js'); 18 | const download = require('../shared/download.js'); 19 | 20 | const updateEngine = async ({ status, name, id, alias }) => { 21 | 22 | const getLatestVersion = require(`../engines/${id}/get-latest-version.js`); 23 | const predictUrl = require(`../engines/${id}/predict-url.js`); 24 | const extract = require(`../engines/${id}/extract.js`); 25 | const test = require(`../engines/${id}/test.js`); 26 | 27 | try { 28 | 29 | console.assert(status.os, '`status.os` is defined'); 30 | 31 | log.start(`Finding the latest ${name} version…`); 32 | const version = await getLatestVersion(status.os); 33 | log.updateSuccess(`Found latest ${name} version: v${version}.`); 34 | 35 | if (status[id] === version) { 36 | log.failure(`${name} v${version} is already installed.`); 37 | return; 38 | } 39 | 40 | log.start(`Predicting URL…`); 41 | const url = predictUrl(version, status.os); 42 | log.updateSuccess(`URL: ${url}`); 43 | 44 | log.start('Downloading…'); 45 | const filePath = await download(url); 46 | log.updateSuccess(`Download completed.`); 47 | 48 | log.start('Extracting…'); 49 | await extract({ 50 | filePath: filePath, 51 | binary: id, 52 | alias: alias, 53 | os: status.os, 54 | }); // Note: this adds output to the log. 55 | log.success(`Extraction completed.`); 56 | 57 | log.start('Testing…'); 58 | await test({ 59 | binary: id, 60 | alias: alias, 61 | }); 62 | log.updateSuccess('Testing completed.'); 63 | 64 | log.success(`${name} v${version} has been installed! 🎉`); 65 | 66 | // Write version data to the status file, so we can later avoid 67 | // reinstalling the same version. 68 | status[id] = version; 69 | setStatus(status); 70 | 71 | } catch (error) { 72 | log.failure(error); 73 | } 74 | 75 | }; 76 | 77 | module.exports = updateEngine; 78 | -------------------------------------------------------------------------------- /shared/get.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const got = require('got'); 17 | 18 | const pkg = require('../package.json'); 19 | 20 | const get = (url, options) => { 21 | return got(url, { 22 | ...options, 23 | headers: { 24 | 'User-Agent': `${pkg.name}/${pkg.version} (+${pkg.homepage})` 25 | }, 26 | }); 27 | }; 28 | 29 | module.exports = get; 30 | -------------------------------------------------------------------------------- /shared/install-specific-version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const { setStatus } = require('../shared/status.js'); 17 | const log = require('../shared/log.js'); 18 | const download = require('../shared/download.js'); 19 | 20 | const installSpecificEngineVersion = async ({ status, name, id, alias }) => { 21 | 22 | const getSpecificVersion = require(`../engines/${id}/get-specific-version.js`); 23 | const predictUrl = require(`../engines/${id}/predict-url.js`); 24 | const extract = require(`../engines/${id}/extract.js`); 25 | const test = require(`../engines/${id}/test.js`); 26 | 27 | try { 28 | 29 | const version = status.version; 30 | log.start(`Finding ${name} v${version}…`); 31 | const fullVersion = await getSpecificVersion(version); 32 | log.updateSuccess(`Found specific ${name} version: v${fullVersion}.`); 33 | 34 | if ( 35 | status.versions && 36 | status.versions[id] && 37 | status.versions[id][version] === fullVersion 38 | ) { 39 | log.failure(`${name} v${fullVersion} is already installed.`); 40 | return; 41 | } 42 | 43 | log.start(`Predicting URL…`); 44 | const os = status.os; 45 | const url = predictUrl(fullVersion, os); 46 | log.updateSuccess(`URL: ${url}`); 47 | 48 | log.start('Downloading…'); 49 | const filePath = await download(url); 50 | log.updateSuccess(`Download completed.`); 51 | 52 | log.start('Extracting…'); 53 | const binary = `${id}-${fullVersion}`; 54 | alias = `${alias}-${fullVersion}`; 55 | await extract({ 56 | filePath: filePath, 57 | binary: binary, 58 | alias: alias, 59 | os: os, 60 | }); // Note: this adds output to the log. 61 | log.success(`Extraction completed.`); 62 | 63 | log.start('Testing…'); 64 | await test({ 65 | binary: binary, 66 | alias: alias, 67 | }); 68 | log.updateSuccess('Testing completed.'); 69 | 70 | log.success(`${name} v${fullVersion} has been installed! 🎉`); 71 | 72 | // Write version data to the status file, so we can later avoid 73 | // reinstalling the same version. 74 | if (status.versions === undefined) { 75 | status.versions = { 76 | [id]: { 77 | [version]: fullVersion, 78 | }, 79 | }; 80 | } else { 81 | if (status.versions[id] === undefined) { 82 | status.versions[id] = { 83 | [version]: fullVersion, 84 | }; 85 | } else { 86 | status.versions[id][version] = fullVersion; 87 | } 88 | } 89 | setStatus(status); 90 | 91 | } catch (error) { 92 | log.failure(error); 93 | } 94 | 95 | }; 96 | 97 | module.exports = installSpecificEngineVersion; 98 | -------------------------------------------------------------------------------- /shared/installer.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fse = require('fs-extra'); 17 | const path = require('path'); 18 | 19 | const glob = require('glob'); 20 | const tildify = require('tildify'); 21 | 22 | const config = require('../shared/config.js'); 23 | const jsvuPath = config.path; 24 | const jsvuBinPath = config.binPath; 25 | 26 | const installSingleBinary = (from, to) => { 27 | console.log(`Installing binary to ${tildify(to)}…`); 28 | // TODO: Figure out why `removeSync` appears to be needed here despite the 29 | // use of `overwrite: true` in `moveSync`. 30 | fse.removeSync(to); 31 | fse.moveSync(from, to, { 32 | overwrite: true, 33 | }); 34 | fse.chmodSync(to, 0o555); 35 | }; 36 | 37 | const installSingleBinarySymlink = (from, to) => { 38 | console.log(`Installing symlink at ${tildify(from)} pointing to ${ 39 | tildify(to)}…`); 40 | fse.ensureSymlinkSync(to, from); 41 | }; 42 | 43 | class Installer { 44 | constructor({ engine, path }) { 45 | this.engine = engine; 46 | this.sourcePath = path; 47 | this.targetRelPath = `engines/${engine}`; 48 | this.targetPath = `${jsvuPath}/${this.targetRelPath}`; 49 | fse.ensureDirSync(this.targetPath); 50 | } 51 | installLibrary(fileName) { 52 | const from = `${this.sourcePath}/${fileName}`; 53 | // Workaround for https://github.com/GoogleChromeLabs/jsvu/issues/81. 54 | if (!fse.existsSync(from) || fse.statSync(from).size === 1) { 55 | return false; 56 | } 57 | const to = `${this.targetPath}/${fileName}`; 58 | console.log(`Installing library to ${tildify(to)}…`); 59 | fse.ensureDirSync(path.dirname(to)); 60 | fse.moveSync(from, to, { 61 | overwrite: true, 62 | }); 63 | return true; 64 | } 65 | installLibraryGlob(pattern) { 66 | const filePaths = glob.sync(`${this.sourcePath}/${pattern}`); 67 | for (const filePath of filePaths) { 68 | const fileName = path.relative(this.sourcePath, filePath); 69 | this.installLibrary(fileName); 70 | } 71 | } 72 | installBinary(arg, options = { symlink: true }) { 73 | if (typeof arg === 'object') { 74 | for (const from of Object.keys(arg)) { 75 | const to = arg[from]; 76 | installSingleBinary( 77 | `${this.sourcePath}/${from}`, 78 | `${this.targetPath}/${to}` 79 | ); 80 | if (options.symlink) { 81 | installSingleBinarySymlink( 82 | `${jsvuBinPath}/${to}`, 83 | `${this.targetPath}/${to}` 84 | ); 85 | } 86 | } 87 | } else { 88 | const from = arg; 89 | installSingleBinary( 90 | `${this.sourcePath}/${from}`, 91 | `${this.targetPath}/${from}` 92 | ); 93 | if (options.symlink) { 94 | installSingleBinarySymlink( 95 | `${jsvuBinPath}/${from}`, 96 | `${this.targetPath}/${from}` 97 | ); 98 | } 99 | } 100 | } 101 | installBinarySymlink(arg) { 102 | if (typeof arg === 'object') { 103 | for (const to of Object.keys(arg)) { 104 | const from = arg[to]; 105 | installSingleBinarySymlink( 106 | `${jsvuBinPath}/${from}`, 107 | `${this.targetPath}/${to}` 108 | ); 109 | } 110 | } else { 111 | const to = arg; 112 | installSingleBinarySymlink( 113 | `${jsvuBinPath}/${to}`, 114 | `${this.targetPath}/${to}` 115 | ); 116 | } 117 | } 118 | installLicense(arg) { 119 | for (const from of Object.keys(arg)) { 120 | const to = `${this.targetPath}/${arg[from]}`; 121 | console.log(`Installing license to ${tildify(to)}…`); 122 | fse.moveSync( 123 | `${this.sourcePath}/${from}`, 124 | to, 125 | { 126 | overwrite: true, 127 | } 128 | ); 129 | } 130 | } 131 | installScript({ name, alias, symlink, generateScript }) { 132 | const to = `${jsvuBinPath}/${name}`; 133 | console.log(`Installing wrapper script to ${tildify(to)}…`); 134 | const wrapperPath = process.platform === 'win32' ? 135 | `%~dp0\\..\\${this.targetRelPath}` : 136 | this.targetPath; 137 | const contents = generateScript(wrapperPath) 138 | .trimStart() 139 | .replaceAll('\t', ''); 140 | fse.removeSync(to); 141 | fse.writeFileSync(to, contents); 142 | fse.chmodSync(to, 0o555); 143 | if (alias) { 144 | if (symlink) { 145 | installSingleBinarySymlink(`${jsvuBinPath}/${alias}`, to); 146 | } else { 147 | fse.copySync(to, `${jsvuBinPath}/${alias}`); 148 | } 149 | } 150 | } 151 | } 152 | 153 | module.exports = { 154 | Installer, 155 | }; 156 | -------------------------------------------------------------------------------- /shared/log.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const chalk = require('chalk'); 17 | 18 | const log = { 19 | failure: (message) => { 20 | console.log(`${chalk.red('✖')} ${message}`); 21 | }, 22 | start: (message) => { 23 | console.log(`${chalk.yellow('❯')} ${message}`); 24 | }, 25 | success: (message) => { 26 | console.log(`${chalk.green('✔')} ${message}`); 27 | }, 28 | updateSuccess: (message) => { 29 | console.log(`\x1B[1A\x1B[K${chalk.green('✔')} ${message}`); 30 | }, 31 | banner: (version) => { 32 | const highlight = chalk.bold.hex('#859901'); 33 | console.log(`📦 jsvu v${version} — the ${highlight('J')}ava${ 34 | highlight('S')}cript engine ${highlight('V')}ersion ${ 35 | highlight('U')}pdater 📦`); 36 | }, 37 | }; 38 | 39 | module.exports = log; 40 | -------------------------------------------------------------------------------- /shared/match-response.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const get = require('./get.js'); 17 | 18 | const matchResponse = ({ url, regex }) => { 19 | return new Promise(async (resolve, reject) => { 20 | try { 21 | const response = await get(url); 22 | const version = regex.exec(response.body)[1]; 23 | resolve(version); 24 | } catch (error) { 25 | reject(error); 26 | } 27 | }); 28 | }; 29 | 30 | module.exports = matchResponse; 31 | -------------------------------------------------------------------------------- /shared/status.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const mkdirp = require('mkdirp'); 19 | 20 | const config = require('../shared/config.js'); 21 | const jsvuPath = config.path; 22 | const jsvuBinPath = config.binPath; 23 | 24 | const statusFilePath = `${jsvuPath}/status.json`; 25 | 26 | const getStatus = () => { 27 | try { 28 | // Upgrade existing, old installations. 29 | mkdirp.sync(jsvuBinPath); 30 | return require(statusFilePath); 31 | } catch (error) { 32 | return {}; 33 | } 34 | }; 35 | 36 | const setStatus = (status) => { 37 | mkdirp.sync(jsvuBinPath); 38 | // Don’t store one-off CLI args in the persistent configuration. 39 | const statusCopy = { ...status }; 40 | delete statusCopy.engine; 41 | delete statusCopy.version; 42 | fs.writeFileSync( 43 | statusFilePath, 44 | JSON.stringify(statusCopy, null, '\t') 45 | ); 46 | }; 47 | 48 | module.exports = { 49 | getStatus, 50 | setStatus, 51 | }; 52 | -------------------------------------------------------------------------------- /shared/unzip.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the “License”); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // . 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an “AS IS” BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | 'use strict'; 15 | 16 | const fs = require('fs'); 17 | 18 | const extractZip = require('extract-zip'); 19 | 20 | const unzip = ({ from, to }) => { 21 | return new Promise((resolve, reject) => { 22 | extractZip(from, { dir: to }, (error) => { 23 | if (error) { 24 | console.log(error); 25 | reject(); 26 | } 27 | resolve(); 28 | }); 29 | }); 30 | }; 31 | 32 | module.exports = unzip; 33 | --------------------------------------------------------------------------------