on master help sign in
Attempt to have no dependencies for npm installer
commita76c07fde858e84b6b95168442368ff51739cfc9 authorEvan Czaplicki <evancz@users.noreply.github.com> dateFri 10 May 2019, 12:55 (7 years ago) parent862bfb6
5 files changed+128−67
installers/npm/bin/elm+15−37 installers/npm/download.js+95 installers/npm/index.js−21 installers/npm/install.js+10 installers/npm/package.json+8−9
installers/npm/bin/elmmode 100644 → 100755+15−37
@@ -1,48 +1,26 @@
11#!/usr/bin/env node
22
3−// This file exists for the benefit of npm users who have --ignore-scripts
4−// enabled. (Enabling this flag globally is a good security measure.)
5−// Since they won't run the post-install hook, the binaries won't be downloaded
6−// and installed.
3+var child_process = require('child_process');
4+var download = require('../download.js');
5+var fs = require('fs');
6+
7+
8+// Some npm users enable --ignore-scripts (a good security measure)
79//
8−// Since this file is included in "bin" in package.json, npm will install
9−// it automatically in a place that should be on the PATH. All the file does
10−// is to download the appropriate binary (just like the post-install hook would
11−// have), replace this file with that binary, and run the binary.
10+// They do not run the post-install hook, so install.js does not run.
1211//
13−// In this way, the first time a user with --ignore-scripts enabled runs this
14−// binary, it will download and install itself, and then run as normal. From
15−// then on, it will run as normal without re-downloading.
12+// This file pretends to be the downloaded binary. It then downloads
13+// the real binary over itself. So in that case, the first run would
14+// also make an HTTPS request to fetch the real binary.
15+
1616
17−var install = require("..").install;
18−var child_process = require("child_process");
19−var path = require("path");
20−var fs = require("fs");
2117
2218// Make sure we get the right path even if we're executing from the symlinked
2319// node_modules/.bin/ executable
24−var interpreter = fs.realpathSync(process.argv[0]);
25−var targetPath = fs.realpathSync(process.argv[1]);
26−
27−// Figure out the binary name as we'll eventually want to execute
28−// this. Re-executing this script doesn't always work because of varying
29−// permissions and modes of operation across platforms (for example, Windows has
30−// some interesting edge cases here.)
31−var binaryName = path.join(
32− __dirname,
33− "..",
34− "unpacked_bin",
35− path.basename(targetPath)
36−);
37−if (process.platform === "win") {
38− binaryName += ".exe";
39−}
40−
41−// cd into the directory above bin/ so install() puts bin/ in the right place.
42−process.chdir(path.join(path.dirname(targetPath), ".."));
20+var pathToThisFile = fs.realpathSync(process.argv[1]);
4321
44−install(process.platform, process.arch).then(function() {
22+download(pathToThisFile, function() {
4523 child_process
46− .spawn(binaryName, process.argv.slice(2), { stdio: "inherit" })
47− .on("exit", process.exit);
24+ .spawn(pathToThisFile, process.argv.slice(2), { stdio: 'inherit' })
25+ .on('exit', process.exit);
4826});
installers/npm/download.jsnew file+95
@@ -0,0 +1,95 @@
1+var fs = require('fs');
2+var package = require('./package.json');
3+var URL = require('url').URL;
4+var https = require('https');
5+var zlib = require('zlib');
6+
7+
8+
9+// MAIN
10+//
11+// This function is used by install.js and by the bin/elm backup that gets
12+// called when --ignore-scripts is enabled. That's why install.js is so weird.
13+
14+
15+module.exports = function(destinationPath, callback)
16+{
17+ var version = package.version.replace(/^(\d+\.\d+\.\d+).*$/, '$1'); // turn '1.2.3-alpha' into '1.2.3'
18+ var os = { 'darwin': 'mac', 'win32': 'windows', 'linux': 'linux' }[process.platform];
19+ var arch = { 'x64': '64-bit', 'ia32': '32-bit' }[process.arch];
20+
21+ download(
22+ destinationPath,
23+ 'https://github.com/elm/compiler/releases/download/' + version + '/binary-for-' + os + '-' + arch + '.gz',
24+ callback
25+ );
26+};
27+
28+
29+
30+// DOWNLOAD
31+
32+
33+function download(destinationPath, url, callback)
34+{
35+ requestWithRedirects(url, 10,
36+ function(error) {
37+ var url = host + '/' + path;
38+ throw new Error('Error fetching binary from ' + url + ': ' + error);
39+ },
40+ function(response) {
41+ if (response.statusCode == 404)
42+ {
43+ // TODO give specific error message
44+ throw new Error('Not Found: ' + url);
45+ }
46+
47+ response.on('error', function() {
48+ throw new Error('Error receiving ' + url);
49+ });
50+
51+ var gunzip = zlib.createGunzip().on('error', function(error) {
52+ throw new Error('Error decompressing elm.gz: ' + error);
53+ });
54+ var write = fs.createWriteStream(destinationPath, {
55+ encoding: 'binary',
56+ mode: 0o755
57+ }).on('finish', callback).on('error', function(error) {
58+ throw new Error('Error writing file: ' + error);
59+ });
60+
61+ response.pipe(gunzip).pipe(write);
62+ }
63+ );
64+}
65+
66+
67+
68+// HANDLE REDIRECTS
69+
70+
71+function requestWithRedirects(url, maxRedirects, failure, success)
72+{
73+ var req = https.request(url);
74+
75+ req.on('response', function(response) {
76+ // Check for redirects
77+
78+ var redirectUrl = new URL(response.headers['location'], new URL(url).origin).href;
79+
80+ if (typeof redirectPath === 'string' && response.statusCode >= 300 && response.statusCode < 400)
81+ {
82+ return (maxRedirects <= 0)
83+ ? failure(new Error('followed 10 redirects and gave up'))
84+ : requestWithRedirects(redirectPath, maxRedirects - 1, failure, success);
85+ }
86+ else
87+ {
88+ return success(response);
89+ }
90+ });
91+
92+ req.on('error', failure);
93+
94+ req.end();
95+}
installers/npm/index.jsdeleted−21
@@ -1,21 +0,0 @@
1−var binwrap = require("binwrap");
2−var path = require("path");
3−
4−var packageInfo = require(path.join(__dirname, "package.json"));
5−// Use major.minor.patch from version string - e.g. "1.2.3" from "1.2.3-alpha"
6−var binVersion = packageInfo.version.replace(/^(\d+\.\d+\.\d+).*$/, "$1");
7−
8−var root =
9− "https://github.com/elm/compiler/releases/download/" +
10− binVersion +
11− "/binaries-for-";
12−
13−module.exports = binwrap({
14− binaries: ["elm"],
15− urls: {
16− "darwin-x64": root + "mac.tar.gz",
17− "win32-x64": root + "windows.tar.gz",
18− "win32-ia32": root + "windows.tar.gz",
19− "linux-x64": root + "linux.tar.gz"
20− }
21−});
installers/npm/install.jsnew file+10
@@ -0,0 +1,10 @@
1+
2+var download = require('./download.js');
3+var package = require('./package.json');
4+var path = require('path');
5+
6+
7+// get slashes right for Windows
8+var targetPath = path.resolve(__dirname, Object.values(package.bin)[0]);
9+
10+download(targetPath, function() {});
installers/npm/package.json+8−9
@@ -1,8 +1,7 @@
11{
22 "name": "elm",
3− "version": "0.19.0-bugfix2",
4− "description": "The Elm Platform: Binaries for the Elm programming language.",
5− "main": "index.js",
3+ "version": "0.19.0-no-deps",
4+ "description": "Installer for Elm: just downloads the binary into node_modules",
65 "preferGlobal": true,
76 "license": "BSD-3-Clause",
87 "repository": {
@@ -17,14 +16,14 @@
1716 "url": "https://github.com/rtfeldman"
1817 },
1918 "engines": {
20− "node": ">=4.0.0"
19+ "node": ">=7.0.0"
2120 },
2221 "scripts": {
23− "install": "binwrap-install",
24− "prepublishOnly": "binwrap-test"
22+ "install": "node install.js"
2523 },
2624 "files": [
27− "index.js",
25+ "install.js",
26+ "download.js",
2827 "bin",
2928 "bin/elm"
3029 ],
@@ -33,12 +32,12 @@
3332 "binary",
3433 "binaries",
3534 "elm",
36− "platform"
35+ "install",
36+ "installer"
3737 ],
3838 "bin": {
3939 "elm": "bin/elm"
4040 },
4141 "dependencies": {
42− "binwrap": "0.1.4"
4342 }
4443}