diff --git a/spec/fixtures/packages/package-with-incompatible-native-module/main.js b/spec/fixtures/packages/package-with-incompatible-native-module/main.js new file mode 100644 index 000000000..e69de29bb diff --git a/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/build/Release/native.node b/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/build/Release/native.node new file mode 100644 index 000000000..e69de29bb diff --git a/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/main.js b/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/main.js new file mode 100644 index 000000000..97f5c264e --- /dev/null +++ b/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/main.js @@ -0,0 +1 @@ +throw new Error("this simulates a native module's failure to load") diff --git a/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/package.json b/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/package.json new file mode 100644 index 000000000..cac262cba --- /dev/null +++ b/spec/fixtures/packages/package-with-incompatible-native-module/node_modules/native-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "native-module", + "main": "./main.js" +} diff --git a/spec/fixtures/packages/package-with-incompatible-native-module/package.json b/spec/fixtures/packages/package-with-incompatible-native-module/package.json new file mode 100644 index 000000000..0f1d6fca7 --- /dev/null +++ b/spec/fixtures/packages/package-with-incompatible-native-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "package-with-incompatible-native-module", + "main": "./main.js" +} diff --git a/spec/package-spec.coffee b/spec/package-spec.coffee index e5b473930..70469d367 100644 --- a/spec/package-spec.coffee +++ b/spec/package-spec.coffee @@ -1,8 +1,17 @@ {$} = require 'atom' path = require 'path' +Package = require '../src/package' ThemePackage = require '../src/theme-package' describe "Package", -> + describe "when the package contains incompatible native modules", -> + it "does not activate it", -> + packagePath = atom.project.resolve('packages/package-with-incompatible-native-module') + pack = new Package(packagePath) + expect(pack.isCompatible()).toBe false + expect(pack.incompatibleModules[0].name).toBe 'native-module' + expect(pack.incompatibleModules[0].path).toBe path.join(packagePath, 'node_modules', 'native-module') + describe "theme", -> theme = null diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 0a4e2457b..c962a0236 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -36,6 +36,7 @@ class PackageManager @loadedPackages = {} @activePackages = {} + @incompatiblePackages = {} @packageStates = {} @packageActivators = [] @@ -159,6 +160,7 @@ class PackageManager pack = new Package(packagePath, metadata) pack.load() @loadedPackages[pack.name] = pack + @incompatiblePackages[pack.name] = pack unless pack.isCompatible() pack catch error console.warn "Failed to load package.json '#{path.basename(packagePath)}'", error.stack ? error diff --git a/src/package.coffee b/src/package.coffee index 53921a609..64ddf1204 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -269,6 +269,7 @@ class Package requireMainModule: -> return @mainModule if @mainModule? + return unless @isCompatible() mainModulePath = @getMainModulePath() @mainModule = require(mainModulePath) if fs.isFileSync(mainModulePath) @@ -340,3 +341,54 @@ class Package for eventHandler in eventHandlers eventHandler.handler = eventHandler.disabledHandler delete eventHandler.disabledHandler + + containsNativeModule: (modulePath) -> + try + fs.listSync(path.join(modulePath, 'build', 'Release'), ['.node']).length > 0 + catch error + false + + getNativeModuleDependencyPaths: -> + nativeModulePaths = [] + + traversePath = (nodeModulesPath) => + try + for modulePath in fs.listSync(nodeModulesPath) + if @containsNativeModule(modulePath) + nativeModulePaths.push(modulePath) + traversePath(path.join(modulePath, 'node_modules')) + + traversePath(path.join(@path, 'node_modules')) + nativeModulePaths + + getIncompatibleNativeModules: -> + incompatibleNativeModules = [] + + for nativeModulePath in @getNativeModuleDependencyPaths() + try + require(nativeModulePath) + catch error + incompatibleNativeModules.push + path: nativeModulePath + name: path.basename(nativeModulePath) + error: error + + incompatibleNativeModules + + # Public: Is this package compatible with this version of Atom? + # Incompatible packages cannot be activated. + # + # Returns a {Boolean}, true if compatible, false if incompatible. + isCompatible: -> + return @compatible if @compatible? + + # Bundled packages are always considered compatible + if @path.indexOf(path.join(atom.packages.resourcePath, 'node_modules') + path.sep) is 0 + @compatible = true + return + + if packageMain = @getMainModulePath() + @incompatibleModules = @getIncompatibleNativeModules() + @compatible = @incompatibleModules.length is 0 + else + @compatible = true