diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..baa91fa --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + presets: ["es2015-riot"] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index d3f11de..f06235c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,2 @@ -# https://git-scm.com/docs/gitignore -# https://help.github.com/articles/ignoring-files -# Example .gitignore files: https://github.com/github/gitignore -/bower_components/ -/node_modules/ \ No newline at end of file +node_modules +dist diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..5475341 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Why is NPM broken? + +This is the source code for the `npm install` error analysis tool at http://cryto.net/why-is-npm-broken/. + +This tool takes `npm install` output, tries to determine what error occurred, and presents it to the user in an easily understable format - where possible, including instructions on how to resolve the issue. + +## Caveats + +* It's not the nicest code. This tool was thrown together in a hurry, and I haven't yet had the time to make it nicer. +* It doesn't yet understand *every possible* error. I add detection rules whenever I run across new error cases in the #Node.js channel and elsewhere. If you run into a new case, please [file an issue](https://git.cryto.net/joepie91/why-is-npm-broken/issues/new). +* While for "missing library" issues it tries to determine *which* library you're missing, the database of known libraries (`src/known-libraries.js`) is currently very limited. Again, if you run into an unrecognized library, please [file an issue](https://git.cryto.net/joepie91/why-is-npm-broken/issues/new). +* The error detection logic in this tool is currently developed in-tree because its output format is still subject to potential changes, but it will eventually be moved into its own module. Hopefully, one day NPM will integrate it into their client directly. + +## Donate + +Maintaining open-source projects takes a lot of time, and the more donations I receive, the more time I can dedicate to open-source. If this module is useful to you, consider [making a donation](http://cryto.net/~joepie91/donate.html)! + +You can donate using Bitcoin, PayPal, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else. Thank you! + +## Contributing + +Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the files in `src/`, not those in `lib/`. + +Build tool of choice is `gulp`; simply run `gulp` while developing, and it will watch for changes. + +Be aware that by making a pull request, you agree to release your modifications under the licenses stated above. diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..7ce010a --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,112 @@ +'use strict'; + +const gulp = require("gulp"); +const path = require("path"); +const presetES2015 = require("@joepie91/gulp-preset-es2015"); +const presetJade = require("@joepie91/gulp-preset-jade"); +const presetSCSS = require("@joepie91/gulp-preset-scss"); +const webpackStream = require("webpack-stream"); +const webpack = require("webpack"); +const rename = require("gulp-rename"); +const nodemon = require("gulp-nodemon"); +const livereload = require("gulp-livereload"); +const net = require("net"); + +var nodemonRestarting = false; + +function tryReload() { + if (nodemonRestarting === false) { + livereload.changed(arguments[0].path); + } +} + +/* The following resolves JacksonGariety/gulp-nodemon#33 */ +process.once("SIGINT", function() { + process.exit(0); +}); + +gulp.task("jade", function() { + return gulp.src("./src/**/*.jade") + .pipe(presetJade({ + basePath: __dirname + })) + .pipe(gulp.dest("./dist")); +}); + +gulp.task("scss", function() { + return gulp.src("./src/**/*.scss") + .pipe(presetSCSS({ + basePath: __dirname + })) + .pipe(gulp.dest("./dist")); +}); + +gulp.task('webpack', function(){ + return gulp.src("./src/index.js") + .pipe(webpackStream({ + watch: true, + module: { + preLoaders: [{ + test: /\.tag$/, + loader: "riotjs-loader", + exclude: /node_modules/, + query: { + type: "babel", + template: "jade" + } + }], + loaders: [ + { test: /\.js$/, loader: "babel-loader" }, + { test: /\.json$/, loader: "json-loader" } + ] + }, + plugins: [ new webpack.ProvidePlugin({riot: "riot"}) ], + resolveLoader: { root: path.join(__dirname, "node_modules") }, + resolve: { + extensions: [ + "", + ".tag", + ".web.js", ".js", + ".web.json", ".json" + ] + }, + debug: false + })) + .pipe(rename("bundle.js")) + .pipe(gulp.dest("./dist")); +}); + +function checkServerUp(){ + setTimeout(function(){ + var sock = new net.Socket(); + sock.setTimeout(50); + sock.on("connect", function(){ + nodemonRestarting = false; + console.log("Triggering page reload..."); + tryReload("*"); + sock.destroy(); + }) + .on("timeout", checkServerUp) + .on("error", checkServerUp) + .connect(4000); + }, 70); +} + +gulp.task("nodemon", ["jade", "scss"], function() { + nodemon({ + script: "./local-server.js", + delay: 500, + ignore: ["dist/*"] + }).on("restart", function() { + nodemonRestarting = true; + }).on("start", checkServerUp); +}) + +gulp.task('watch', ["nodemon"], function () { + livereload.listen(); + gulp.watch("./src/**/*.scss", ["scss"]); + gulp.watch("./src/**/*.jade", ["jade"]); + gulp.watch(['./dist/**/*']).on('change', tryReload); +}); + +gulp.task("default", ["watch", "webpack"]); \ No newline at end of file diff --git a/local-server.js b/local-server.js new file mode 100644 index 0000000..91145c6 --- /dev/null +++ b/local-server.js @@ -0,0 +1,9 @@ +'use strict'; + +const express = require("express"); + +let app = express(); + +app.use(express.static("./dist")); + +app.listen(4000); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..4a6a1ca --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "why-is-npm-broken", + "version": "1.0.0", + "description": "A simple tool to determine why your `npm install` doesn't work", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "deploy": "rsync -avz --progress ./dist/* root@cryto.net:/var/www/why-is-npm-broken/" + }, + "repository": { + "type": "git", + "url": "git@git.cryto.net:joepie91/why-is-npm-broken.git" + }, + "keywords": [ + "npm" + ], + "author": "Sven Slootweg", + "license": "WTFPL", + "dependencies": { + "@joepie91/gulp-preset-es2015": "^1.0.1", + "arr-flatten": "^1.0.1", + "dedupe": "^2.0.3", + "in-array": "^0.1.2", + "jquery": "^3.1.0", + "riot": "^2.5.0", + "riot-query": "^1.0.0" + }, + "devDependencies": { + "@joepie91/gulp-preset-jade": "^1.0.1", + "@joepie91/gulp-preset-scss": "^1.0.1", + "babel-loader": "^6.2.4", + "babel-preset-es2015-riot": "^1.1.0", + "babel-preset-es2015": "^6.9.0", + "express": "^4.14.0", + "gulp": "^3.9.1", + "gulp-livereload": "^3.8.1", + "gulp-nodemon": "^2.1.0", + "gulp-rename": "^1.2.2", + "json-loader": "^0.5.4", + "riotjs-loader": "^3.0.0", + "webpack": "^1.13.1", + "webpack-stream": "^3.2.0" + } +} diff --git a/src/analyze-error.js b/src/analyze-error.js new file mode 100644 index 0000000..af41fff --- /dev/null +++ b/src/analyze-error.js @@ -0,0 +1,196 @@ +'use strict'; + +const regexExecAll = require("./regex/exec-all"); + +function isMissingBuildEnvironment(code) { + return /Windows SDK version [0-9.]+ was not found/.test(code); +} + +function isBrokenBuildEnvironment(code) { + // ex. https://stackoverflow.com/questions/31936170/npm-the-ld-library-not-found-for-lgcc-s-10-5-on-on-os-x-el-capitain + return (code.indexOf("library not found for -lgcc_s.10.5") !== -1); +} + +function isMissingPython(code) { + // testcases/1.txt + return ( + code.indexOf("Can't find Python executable") !== -1 || + /Command failed:.+python\.exe/i.test(code) + ); +} + +function isWrongPython(code) { + return /Python executable "[^"]*" is v[^,]+, which is not supported by gyp./.test(code); +} + +function isBindingSyntaxError(code) { + // ex. https://github.com/nodejs/node-gyp/issues/979 + return /File "binding.gyp",[\s\S]+SyntaxError: invalid syntax/.test(code); +} + +function isMissingNodeHeaders(code) { + return (code.indexOf("common.gypi not found") !== -1); +} + +function isGCCVersionError(code) { + // ex. https://github.com/nodejs/node-gyp/issues/233#issuecomment-181912859 + return (code.indexOf("requires a C++11 compiler") !== -1); +} + +function isNanError(code) { + // ex. http://pastebin.com/SzdS5YBa + return (code.indexOf("NanNew") !== -1) || /Nan::[a-zA-Z]+/.test(code); +} + +function isV8Error(code) { + return /V8::[a-zA-Z]+/i.test(code); +} + +function isSymlinkIssue(code) { + // ex. http://pastebin.com/ASxGthw2 + return /npm ERR\!.+\.bin'/.test(code); +} + +function isNPMBug(code) { + return (code.indexOf("file already exists, mkdir") !== -1); +} + +function isUnmetDependency(code) { + // ex. http://pastebin.com/ASxGthw2 + return (code.indexOf("UNMET DEPENDENCY") !== -1); +} + +function isUnmetPeerDependency(code) { + // ex. http://pastebin.com/ASxGthw2 + return (code.indexOf("UNMET PEER DEPENDENCY") !== -1); +} + +let missingLibraryRegex = /(?:(?:fatal error|warning): ([^\.]+\.h): No such file or directory|Package (.+) was not found in the pkg-config search path)/g +let missingDependencyRegex = /UNMET DEPENDENCY ([^@]+)@(.+)/g +let missingPeerDependencyRegex = /UNMET PEER DEPENDENCY ([^@]+)@(.+)/g + +function isLibraryMissingError(code) { + // ex. https://github.com/JustinTulloss/zeromq.node/issues/456 + // ex. https://github.com/Automattic/node-canvas/issues/246 + return missingLibraryRegex.test(code); +} + +function getMissingLibraries(code) { + // alternatively: include <([^>]+)> + return regexExecAll(missingLibraryRegex, code).map((match) => match[1] || match[2]); +} + +function getMissingDependencies(code) { + // ex. http://pastebin.com/ASxGthw2 + return regexExecAll(missingDependencyRegex, code).map((match) => { + return { + name: match[1], + version: match[2] + }; + }); +} + +function getMissingPeerDependencies(code) { + // ex. http://pastebin.com/ASxGthw2 + return regexExecAll(missingPeerDependencyRegex, code).map((match) => { + return { + name: match[1], + version: match[2] + }; + }); +} + +function isLibraryVersionError(code) { + return /binding.cc:[0-9]+: error/.test(code); +} + +function getOffendingModule(code) { + let match = /Failed at the ([^@]+)@[^\s]+ install script '.+'.\n/.exec(code); + + if (match != null) { + return match[1]; + } else { + // Try to determine the offending module from the node-gyp working directory. + // This is useful if eg. the user only copied the `gyp` part of the output. + let match = /gyp ERR! cwd .+[\/\\]node_modules[\/\\]([^\/\\\n]+).*\n/.exec(code); + + if (match != null) { + return match[1]; + } else { + throw new Error("Could not determine the offending module"); + } + } +} + +function getErrorType(code) { + if (isMissingBuildEnvironment(code)) { + return "missingEnvironment"; + } else if (isBrokenBuildEnvironment(code)) { + return "brokenEnvironment"; + } else if (isMissingPython(code)) { + return "missingPython"; + } else if (isWrongPython(code)) { + return "wrongPython"; + } else if (isBindingSyntaxError(code)) { + return "bindingSyntax"; + } else if (isMissingNodeHeaders(code)) { + return "headers"; + } else if (isGCCVersionError(code)) { + return "gcc"; + } else if (isLibraryMissingError(code)) { + return "missingLibrary"; + } else if (isNanError(code)) { + return "nan"; + } else if (isV8Error(code)) { + return "v8"; + } else if (isLibraryVersionError(code)) { + return "libraryVersion"; + } else if (isSymlinkIssue(code)) { + return "symlinkIssue"; + } else if (isNPMBug(code)) { + return "npmBug"; + } else if (isUnmetDependency(code)) { + return "unmetDependency"; + } else if (isUnmetPeerDependency(code)) { + return "unmetPeerDependency"; + } else { + throw new Error("Unknown error"); + } +} + +module.exports = function(code) { + let type = getErrorType(code); + + let errorObject = { + type: type, + data: {} + } + + if (type === "missingLibrary") { + Object.assign(errorObject.data, { + missingLibraries: getMissingLibraries(code) + }); + } + + if (type === "unmetDependency") { + Object.assign(errorObject.data, { + missingModules: getMissingDependencies(code) + }); + } + + if (type === "unmetPeerDependency") { + Object.assign(errorObject.data, { + missingModules: getMissingPeerDependencies(code) + }); + } + + try { + Object.assign(errorObject.data, { + offendingModule: getOffendingModule(code) + }); + } catch (err) { + // do nothing... + } + + return errorObject; +} diff --git a/src/components/app.tag b/src/components/app.tag new file mode 100644 index 0000000..8651322 --- /dev/null +++ b/src/components/app.tag @@ -0,0 +1,145 @@ +app + .header + h1 Why is NPM broken? + + .contents + p When something breaks in NPM, the error output usually isn't very clear about the cause. When you provide the full output, I will attempt to determine what is causing your problem, and propose a solution. + + p This tool is entirely client-side and your input will never be sent to a server, so you don't need to redact information from your log. It's also open-source - you can find the source code for it here. + + .important + p Enter the full output of your npm install command, from start to end. Otherwise, I might not be able to find your problem. + p Output from npm-debug.log will not usually work - make sure that you copy the output from your terminal. + + codebox + + hr + + solution-viewer + + style(type="scss"). + body { + font-family: sans-serif; + background-color: white; + margin: 0; + padding: 0; + line-height: 1.4; + } + + .contents, .header { + max-width: 960px; + margin: 0px auto; + padding: 14px 16px; + } + + .contents { + background-color: #dddddd; + } + + .header { + background-color: #b40000; + color: white; + + h1 { + margin: 0; + } + } + + .important { + background-color: #ffffa2; + border: 1px solid #c7c702; + border-radius: 4px; + padding: 6px 9px; + margin-bottom: 12px; + + p { + margin: 6px 0px; + } + } + + hr { + height: 1px; + border: 0; + background: #b40000; + margin: 32px 0px; + } + + script. + const $ = require("jquery"); + const analyzeError = require("../analyze-error"); + + this.mixin(require("riot-query").mixin); + + this.queryOne("codebox").on("submit", (code) => { + try { + let results = analyzeError(code); + this.queryOne("solution-viewer").viewTopic(results.type, results.data); + } catch (err) { + this.queryOne("solution-viewer").viewTopic(false, {}); + } + }); + + /* + this.on("mount", () => { + this.queryOne("codebox").setCode(`capaj@kukan:~/WebstormProjects/Q.OUT$ gcc -v + Using built-in specs. + COLLECT_GCC=gcc + COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper + Target: x86_64-linux-gnu + Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu + Thread model: posix + gcc version 4.9.2 (Debian 4.9.2-10) + capaj@kukan:~/WebstormProjects/Q.OUT$ npm i zmq -S + + > zmq@2.13.0 install /home/capaj/WebstormProjects/Q.OUT/node_modules/zmq + > node-gyp rebuild + + make: Entering directory '/home/capaj/WebstormProjects/Q.OUT/node_modules/zmq/build' + CXX(target) Release/obj.target/zmq/binding.o + ../binding.cc:28:17: fatal error: zmq.h: No such file or directory + ../binding.cc:28:17: fatal error: zmq.h: No such file or directory + ../binding.cc:28:17: fatal error: foo.h: No such file or directory + #include + ^ + compilation terminated. + zmq.target.mk:90: recipe for target 'Release/obj.target/zmq/binding.o' failed + make: *** [Release/obj.target/zmq/binding.o] Error 1 + make: Leaving directory '/home/capaj/WebstormProjects/Q.OUT/node_modules/zmq/build' + gyp ERR! build error + gyp ERR! stack Error: \`make\` failed with exit code: 2 + gyp ERR! stack at ChildProcess.onExit (/home/capaj/.nvm/versions/node/v4.1.0/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:270:23) + gyp ERR! stack at emitTwo (events.js:87:13) + gyp ERR! stack at ChildProcess.emit (events.js:172:7) + gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:200:12) + gyp ERR! System Linux 3.16.0-4-amd64 + gyp ERR! command "/home/capaj/.nvm/versions/node/v4.1.0/bin/node" "/home/capaj/.nvm/versions/node/v4.1.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild" + gyp ERR! cwd /home/capaj/WebstormProjects/Q.OUT/node_modules/zmq + gyp ERR! node -v v4.1.0 + gyp ERR! node-gyp -v v3.0.3 + gyp ERR! not ok + npm WARN EPACKAGEJSON q.out@1.0.0 No license field. + npm ERR! Linux 3.16.0-4-amd64 + npm ERR! argv "/home/capaj/.nvm/versions/node/v4.1.0/bin/node" "/home/capaj/.nvm/versions/node/v4.1.0/bin/npm" "i" "zmq" "-S" + npm ERR! node v4.1.0 + npm ERR! npm v3.3.4 + npm ERR! code ELIFECYCLE + + npm ERR! zmq@2.13.0 install: \`node-gyp rebuild\` + npm ERR! Exit status 1 + npm ERR! + npm ERR! Failed at the zmq@2.13.0 install script 'node-gyp rebuild'. + npm ERR! This is most likely a problem with the zmq package, + npm ERR! not with npm itself. + npm ERR! Tell the author that this fails on your system: + npm ERR! node-gyp rebuild + npm ERR! You can get their info via: + npm ERR! npm owner ls zmq + npm ERR! There is likely additional logging output above. + + npm ERR! Please include the following file with any support request: + npm ERR! /home/capaj/WebstormProjects/Q.OUT/npm-debug.log + `); + + $("form", this.queryOne("codebox").root).submit(); + }) + */ diff --git a/src/components/codebox.tag b/src/components/codebox.tag new file mode 100644 index 0000000..d1dcb64 --- /dev/null +++ b/src/components/codebox.tag @@ -0,0 +1,44 @@ +codebox + form(onsubmit="{_handleSubmit}") + .code-container + textarea.code + + .button-container + button Find my problem! + + style(scoped, type="scss"). + .code-container { + + } + + textarea.code { + width: 100%; + height: 190px; + border-color: #b40000; + margin-bottom: 16px; + } + + .button-container { + text-align: center; + } + + button { + background-color: #b40000; + padding: 16px 24px; + font-size: 24px; + border-radius: 5px; + color: white; + font-weight: bold; + } + + script. + const $ = require("jquery"); + + Object.assign(this, { + _handleSubmit: (event) => { + this.trigger("submit", $(".code", this.root).val()); + }, + setCode: (code) => { + $(".code", this.root).val(code); + } + }); \ No newline at end of file diff --git a/src/components/library-box.tag b/src/components/library-box.tag new file mode 100644 index 0000000..7f96165 --- /dev/null +++ b/src/components/library-box.tag @@ -0,0 +1,58 @@ +library-box + .libraryBox + virtual(if="{opts.libraryInfo.name != null}") + h4 {opts.libraryInfo.name} + virtual(if="{opts.libraryInfo.name == null}") + h4 Unknown library + + .filename(each="{headerFile in opts.libraryInfo.headerNames}") {headerFile} + + virtual(if="{opts.libraryInfo.packageNames.length > 0}") + p Known package names for this library: + ul + li(each="{package in opts.libraryInfo.packageNames}") + strong {package.name} + span.distributions ({package.distributions.join(", ")}) + + style(scoped, type="scss"). + .libraryBox { + padding: 12px; + background-color: #e4a6a6; + margin-bottom: 6px; + margin-left: 6px; + font-size: 15px; + width: 49%; + box-sizing: border-box; + float: left; + box-shadow: 3px 3px 8px 0px #270000; + + } + + h4 { + margin-top: 0px; + margin-bottom: 9px; + font-size: 17px; + } + + ul, p { + margin-top: 9px; + margin-bottom: 0px; + } + + li { + margin-bottom: 3px; + } + + .filename { + display: inline-block; + font-size: 13px; + padding: 4px 7px; + background-color: #b40000; + border-radius: 6px; + color: white; + } + + .distributions { + font-size: 14px; + margin-left: 6px; + } \ No newline at end of file diff --git a/src/components/module-box.tag b/src/components/module-box.tag new file mode 100644 index 0000000..f0891aa --- /dev/null +++ b/src/components/module-box.tag @@ -0,0 +1,47 @@ +module-box + .moduleBox + .name {name} + .version {version} + + + script. + this.on("update", () => { + Object.assign(this, { + name: opts.moduleInfo.name, + version: opts.moduleInfo.version + }); + }); + style(scoped, type="scss"). + .moduleBox { + padding: 12px; + background-color: #e4a6a6; + margin-bottom: 6px; + margin-left: 6px; + font-size: 15px; + width: 32%; + box-sizing: border-box; + float: left; + box-shadow: 3px 3px 8px 0px #270000; + + } + + .name, .version { + display: inline-block; + } + + .name { + font-weight: bold; + font-size: 16px; + } + + .version { + font-size: 13px; + + &:before { + content: "("; + } + + &:after { + content: ")"; + } + } \ No newline at end of file diff --git a/src/components/solution-viewer.tag b/src/components/solution-viewer.tag new file mode 100644 index 0000000..bdd693f --- /dev/null +++ b/src/components/solution-viewer.tag @@ -0,0 +1,206 @@ +solution-viewer + .solutions(show="{solution != null && solution !== false}") + h2 This is probably your problem: + + solution(topic="bindingSyntax") + h3 There's a syntax error in the binding.gyp file of the {parent.offendingModule} module. + p Unfortunately, there's nothing you can do about this, other than filing a bug on the {parent.offendingModule} package and waiting for the author to fix it. + + solution(topic="missingEnvironment") + h3 Your build environment is missing or incomplete. + p One or more build tools or SDKs are missing from your system. Follow the setup instructions for node-gyp to resolve this. + + solution(topic="brokenEnvironment") + h3 Your build environment is broken. + p Something in your environment doesn't work as it should; it's possible that you're missing some build tools, or that they need to be updated or rebuilt. + p Try the following steps: + + ol + li Follow the setup instructions for node-gyp. + li If on OS X, make sure to upgrade your installation of XCode to the latest version. + li If it still doesn't work, try reinstalling Node.js. + + solution(topic="missingPython") + h3 Python is not installed. + p You will need to have Python installed, to be able to install compiled (C++) modules with NPM. It's possible that you're also missing other build tools. + p To solve this problem, follow the setup instructions for node-gyp. + + solution(topic="wrongPython") + h3 You have the wrong version of Python installed. + p The node-gyp utility, which is used for installing compiled (C++) modules with NPM, only supports Python 2. However, you only seem to have Python 3 installed. + p To solve this problem, follow the setup instructions for node-gyp. + p If you're certain that you have Python 2 installed, you may need to configure npm to know where it is. This is also covered in the above instructions. + + solution(topic="gcc") + h3 Your C++ compiler is too old. + p Newer C++ modules for Node.js require a recent C++ compiler, and not all distributions include a recent enough version of GCC. + p The exact instructions will vary by distribution and OS, but these are some that I know of: + + ul + li: a(href="http://charette.no-ip.com:81/programming/2011-12-24_GCCv47/") Ubuntu + + p If you know of instructions for other distributions, then please send me an e-mail! + + solution(topic="nan") + h3 Your version of Node.js is too new, and you need to update your dependencies. + p The version of the {parent.offendingModule} module that you're trying to install, was written for an older Node.js version. There's probably a newer version available, so try updating your project or its dependencies. + p If this doesn't work, then file a bug on the {parent.offendingModule} module, and ask the author to make it compatible with the newest Node.js version. + + solution(topic="v8") + h3 Your version of Node.js is either too new, or too old. + p The version of the {parent.offendingModule} module that you're trying to install, was written for a different Node.js version. + p There may be a different version available that's compatible with your Node.js version. + p If there isn't, or you can't find it, then file a bug on the {parent.offendingModule} module, and ask the author to make it compatible with the newest Node.js version. + + solution(topic="missingLibrary", data-handler="{_missingLibraryHandler}") + h3 You're missing one or more libraries on your system. + p A dependency you're trying to install ({parent.offendingModule}) requires one or more libraries to be installed on your system, but NPM can't find them. + p The missing libraries are: + + .libraryBoxes + library-box(each="{library in parent.missingLibraries}", library-info="{library}") + .clear + + virtual(if="{parent.hasUnknownLibraries}") + p For the unknown libraries listed above, try reading the documentation of the {parent.offendingModule} module, or Googling the filename(s). + p To help improve this tool, please e-mail me with the details once you've determined which package(s) the libraries belong to, and I'll add them to the database. + + solution(topic="libraryVersion") + h3 You have the wrong version of a library installed on your system. + p One or more of the libraries that's used by the {parent.offendingModule} module exist on your system, but aren't the right version. + p Unfortunately I don't really have more information about it - you'll have to read the documentation for the {parent.offendingModule} module to determine which version you need to have installed. + p Alternatively, you can e-mail me with a gist of your NPM output, and I'll try to find the problem for you. + + solution(topic="headers") + h3 You're missing the Node.js development headers. + p When installing Node.js from your distribution's package manager, you must also install the development headers. Some possible names of the package for this: + ul + li: code node-dev + li: code node-devel + li: code nodejs-dev + li: code nodejs-devel + p Depending on the distribution you're using, it may also have a different name. + + solution(topic="npmBug") + h3 That looks like an NPM bug. + p Occasionally, NPM installations will bug out, and it seems like that has happened here. Try removing your node_modules directory, and installing your dependencies again from scratch using npm install. + p Make sure that your package.json correctly declares all your dependencies, or there might be missing dependencies. This article explains how to install packages in such a way that this is never an issue. + + solution(topic="symlinkIssue") + h3 There's an issue with symbolic links on your system. + p You are most likely running npm install in something like Docker or VirtualBox, with "shared directories" enabled. This is known to cause issues with symbolic links on some platforms. + p A workaround is to install your packages using --no-bin-links. + + solution(topic="unmetDependency", data-handler="{_missingModuleHandler}") + h3 You're missing a dependency. + p Your package.json indicates a dependency that isn't actually installed into your node_modules. These are the dependencies you're missing: + + .moduleBoxes + module-box(each="{module in parent.missingModules}", module-info="{module}") + .clear + + p You can generally fix this by simply running npm install, without any additional arguments. + + solution(topic="unmetPeerDependency", data-handler="{_missingModuleHandler}") + h3 You're missing a peer dependency. + p You're missing one or more dependencies that require manual installation - usually, this concerns a library that you've installed a plugin for. The missing dependencies are: + + .moduleBoxes + module-box(each="{module in parent.missingModules}", module-info="{module}") + .clear + + p To understand the range notation that is used here (if any), read this article. + + h2 Did this solve your problem? + + p Maintaining open-source projects takes a lot of time and effort. If this tool saved you some headaches, consider making a donation :) + + h2 Still broken? + + p If it still doesn't work, please file an issue, containing the full npm install output. + + //p If it still doesn't work, contact me and I'll try to work out what's wrong. Please paste your output to a pastebin like GitHub Gist, and send it to me on IRC or by e-mail. + + //p IRC: You can find me on Freenode in #Node.js as joepie91. + + //p E-mail: My e-mail address is admin@cryto.net. + + .no-solutions(show="{solution === false}") + h2 Sorry, I couldn't find your problem. + + p Please file an issue, containing the full npm install output. I will try to figure out your error, and add detection for it to this tool as well! + + //p Please paste your output to a pastebin like GitHub Gist, and send it to me on IRC or by e-mail. I will try to figure out your error, and add detection for it to this tool as well! + + //p IRC: You can find me on Freenode in #Node.js as joepie91. + + //p E-mail: My e-mail address is admin@cryto.net. + + style(scoped, type="scss"). + h2 { + margin-top: 32px; + } + + solution { + display: none; + } + + .libraryBoxes, .moduleBoxes { + margin-top: 12px; + margin-bottom: 12px; + } + + .clear { + clear: both; + } + + script. + const $ = require("jquery"); + const flatten = require("arr-flatten"); + const dedupe = require("dedupe"); + const findPackages = require("../find-packages"); + + this.mixin(require("riot-query").mixin); + + Object.assign(this, { + viewTopic: (topic, data) => { + let solutions = this.query("solution"); + + this.solution = topic; + + solutions.forEach((solution) => { + $(solution.root).hide(); + }); + + solutions.filter((solution) => { + return solution.opts.topic === topic; + }).forEach((solution) => { + $(solution.root).show(); + solution.setData(data); + this.offendingModule = data.offendingModule; + }); + + this.update(); + }, + _missingLibraryHandler: (data) => { + this.missingLibraries = dedupe(flatten(data.missingLibraries.map((library) => { + let knownPackages = findPackages(library); + + if (knownPackages.length > 0) { + return knownPackages; + } else { + return { + headerNames: [library] + } + } + }))); + + this.hasUnknownLibraries = (this.missingLibraries.some((library) => library.name == null)); + + this.update(); + }, + _missingModuleHandler: (data) => { + this.missingModules = data.missingModules; + this.update(); + } + }); diff --git a/src/components/solution.tag b/src/components/solution.tag new file mode 100644 index 0000000..8d96769 --- /dev/null +++ b/src/components/solution.tag @@ -0,0 +1,32 @@ +solution + .solution + != "" + + style(scoped, type="scss"). + .solution { + padding: 16px; + border: 1px solid #b40000; + border-radius: 4px; + background-color: #dec8c8; + + h3 { + margin-top: 0px; + } + + p { + margin-bottom: 0px; + } + } + + pre { + padding-left: 16px; + } + + script. + Object.assign(this, { + setData: (data) => { + if (this.opts.dataHandler != null) { + this.opts.dataHandler(data); + } + } + }) \ No newline at end of file diff --git a/src/find-packages.js b/src/find-packages.js new file mode 100644 index 0000000..8ee3c0c --- /dev/null +++ b/src/find-packages.js @@ -0,0 +1,8 @@ +'use strict'; + +const inArray = require("in-array"); +const knownLibraries = require("./known-libraries"); + +module.exports = function findPackages(libraryName) { + return knownLibraries.filter((library) => inArray(library.headerNames, libraryName)); +} \ No newline at end of file diff --git a/src/index.jade b/src/index.jade new file mode 100644 index 0000000..56b5ea4 --- /dev/null +++ b/src/index.jade @@ -0,0 +1,8 @@ +html + head + meta(charset="UTF-8") + title Why is NPM broken? + link(rel="stylesheet", href="style.css") + script(src="bundle.js") + body + app diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..07c492b --- /dev/null +++ b/src/index.js @@ -0,0 +1,20 @@ +'use strict'; + +const $ = require("jquery"); +const riot = require("riot"); +const knownLibraries = require("./known-libraries"); + +require("./components/app"); +require("./components/codebox"); +require("./components/library-box"); +require("./components/module-box"); +require("./components/solution-viewer"); +require("./components/solution"); + +riot.util.tmpl.errorHandler = function (err) { + console.error(err.message + ' in ' + err.riotData.tagName, err.stack); +} + +$(() => { + riot.mount("app"); +}); diff --git a/src/known-libraries.js b/src/known-libraries.js new file mode 100644 index 0000000..745d819 --- /dev/null +++ b/src/known-libraries.js @@ -0,0 +1,50 @@ +'use strict'; + +module.exports = [{ + name: "ZeroMQ", + headerNames: ["zmq.h"], + packageNames: [{ + name: "libzmq-dev", + distributions: [ + "Ubuntu", + "Debian" + ] + }, { + name: "zeromq-devel", + distributions: [ + "openSUSE", + "Fedora", + "RHEL", + "Cygwin" + ] + }, { + name: "zeromq", + distributions: [ + "Arch" + ] + }] +}, { + name: "ICU", + headerNames: ["unicode/ucsdet.h"], + packageNames: [{ + name: "libicu-dev", + distributions: [ + "Ubuntu", + "Debian" + ] + }, { + name: "libicu-devel", + distributions: [ + "openSUSE", + "Fedora", + "CentOS" + ] + }, { + name: "icu", + distributions: [ + "NixOS", + "Arch" + ] + }] + +}] diff --git a/src/regex/exec-all.js b/src/regex/exec-all.js new file mode 100644 index 0000000..21faa41 --- /dev/null +++ b/src/regex/exec-all.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function regexExecAll(regex, target) { + let matches = []; + let match; + + regex.lastIndex = 0; + + while (match = regex.exec(target)) { + matches.push(match); + } + + return matches; +} \ No newline at end of file diff --git a/src/style.scss b/src/style.scss new file mode 100644 index 0000000..e69de29 diff --git a/testcases/1.txt b/testcases/1.txt new file mode 100644 index 0000000..e24368d --- /dev/null +++ b/testcases/1.txt @@ -0,0 +1,41 @@ +gyp ERR! configure error +gyp ERR! stack Error: Can't find Python executable "C:\Users\xxxxxxx\AppData\Local\. +mt-os.windows.x86_32\dev_bundle\python\python.exe", you can set the PYTHON env varia +gyp ERR! stack at failNoPython (C:\Users\xxxxxxx\AppData\Local\.meteor\packages\ +6_32\dev_bundle\lib\node_modules\npm\node_modules\node-gyp\lib\configure.js:449:14) +gyp ERR! stack at C:\Users\xxxxxxx\AppData\Local\.meteor\packages\meteor-tool\1. +e\lib\node_modules\npm\node_modules\node-gyp\lib\configure.js:404:11 +gyp ERR! stack at FSReqWrap.oncomplete (fs.js:82:15) +gyp ERR! System Windows_NT 10.0.10240 +gyp ERR! command "C:\\Users\\xxxxxxx\\AppData\\Local\\.meteor\\packages\\meteor-tool +_bundle\\bin\\node.exe" "C:\\Users\\xxxxxxx\\AppData\\Local\\.meteor\\packages\\mete +32\\dev_bundle\\lib\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "r +gyp ERR! cwd C:\Users\xxxxxxx\Documents\OnsenUI-Meteor-ToDo\node_modules\bcrypt +gyp ERR! node -v v4.6.0 +gyp ERR! node-gyp -v v3.4.0 +gyp ERR! not ok +npm ERR! Windows_NT 10.0.10240 +npm ERR! argv "C:\\Users\\xxxxxxx\\AppData\\Local\\.meteor\\packages\\meteor-tool\\1 +ndle\\bin\\node.exe" "C:\\Users\\xxxxxxx\\AppData\\Local\\.meteor\\packages\\meteor- +\dev_bundle\\lib\\node_modules\\npm\\bin\\npm-cli.js" "--save" "install" "bcrypt" +npm ERR! node v4.6.0 +npm ERR! npm v3.10.8 +npm ERR! code ELIFECYCLE + +npm ERR! bcrypt@0.8.7 install: `node-gyp rebuild` +npm ERR! Exit status 1 +npm ERR! +npm ERR! Failed at the bcrypt@0.8.7 install script 'node-gyp rebuild'. +npm ERR! Make sure you have the latest version of node.js and npm installed. +npm ERR! If you do, this is most likely a problem with the bcrypt package, +npm ERR! not with npm itself. +npm ERR! Tell the author that this fails on your system: +npm ERR! node-gyp rebuild +npm ERR! You can get information on how to open an issue for this project with: +npm ERR! npm bugs bcrypt +npm ERR! Or if that isn't available, you can get their info via: +npm ERR! npm owner ls bcrypt +npm ERR! There is likely additional logging output above. + +npm ERR! Please include the following file with any support request: +npm ERR! C:\Users\xxxxxxx\Documents\OnsenUI-Meteor-ToDo\npm-debug.log