Permalink
Browse files

feat(package-analyzer): support packages without package.json files

closes #575
  • Loading branch information...
JeroenVinke committed Apr 7, 2017
1 parent 324f3e1 commit c225bb7381a85e71051c637c7a33c94b9789ed43
Showing with 199 additions and 11 deletions.
  1. +25 −11 lib/build/package-analyzer.js
  2. +174 −0 spec/lib/build/package-analyzer.spec.js
@@ -34,9 +34,13 @@ exports.PackageAnalyzer = class {
function loadPackageMetadata(project, description) {
return setLocation(project, description)
- .then(() => fs.readFile(description.metadataLocation))
- .then(data => {
- description.metadata = JSON.parse(data.toString());
+ .then(() => {
+ if (description.metadataLocation) {
+ return fs.readFile(description.metadataLocation)
+ .then(data => {
+ description.metadata = JSON.parse(data.toString());
+ });
+ }
})
.catch(e => {
console.log(`Unable to load package metadata (package.json) of ${description.name}:`);
@@ -49,16 +53,20 @@ function determineLoaderConfig(project, description) {
let location = path.resolve(description.location);
let sourcePath;
- if (metadata.jspm) {
- let jspm = metadata.jspm;
+ if (metadata) {
+ if (metadata.jspm) {
+ let jspm = metadata.jspm;
- if (jspm.directories && jspm.directories.dist) {
- sourcePath = path.join(location, jspm.directories.dist, jspm.main);
+ if (jspm.directories && jspm.directories.dist) {
+ sourcePath = path.join(location, jspm.directories.dist, jspm.main);
+ } else {
+ sourcePath = path.join(location, metadata.main);
+ }
} else {
sourcePath = path.join(location, metadata.main);
}
} else {
- sourcePath = path.join(location, metadata.main);
+ sourcePath = path.join(location, 'index');
}
sourcePath = path.relative(path.resolve(project.paths.root), sourcePath);
@@ -75,7 +83,8 @@ function setLocation(project, description) {
return getPackageFolder(project, description)
.then(packageFolder => {
description.location = packageFolder;
- description.metadataLocation = path.join(description.location, 'package.json');
+
+ return tryFindMetadata(project, description);
});
case 'custom':
if (!description.loaderConfig || !description.loaderConfig.packageRoot) {
@@ -85,14 +94,19 @@ function setLocation(project, description) {
}
description.location = path.resolve(project.paths.root, description.loaderConfig.packageRoot);
- description.metadataLocation = path.join(description.location, 'package.json');
- return Promise.resolve();
+ return tryFindMetadata(project, description);
default:
return Promise.reject(`The package source "${description.source}" is not supported.`);
}
}
+function tryFindMetadata(project, description) {
+ return fs.stat(path.join(description.location, 'package.json'))
+ .then(() => description.metadataLocation = path.join(description.location, 'package.json'))
+ .catch(() => {});
+}
+
function getPackageFolder(project, description) {
if (!description.loaderConfig || !description.loaderConfig.path) {
return lookupPackageFolderDefaultStrategy(project.paths.root)
@@ -0,0 +1,174 @@
+'use strict';
+
+const path = require('path');
+const PackageAnalyzer = require('../../../lib/build/package-analyzer').PackageAnalyzer;
+
+describe('The PackageAnalyzer', () => {
+ let mockfs;
+ let project;
+ let sut;
+
+ beforeEach(() => {
+ mockfs = require('mock-fs');
+
+ project = {
+ paths: {
+ root: './src/'
+ }
+ };
+
+ sut = new PackageAnalyzer(project);
+
+ const fsConfig = {};
+ mockfs(fsConfig);
+ });
+
+ afterEach(() => {
+ mockfs.restore();
+ });
+
+ it('sets source to npm when node_modules is found in the path', done => {
+ // setup mock package.json
+ const fsConfig = {};
+ fsConfig[path.join('node_modules/my-package', 'package.json')] = '{ }';
+ mockfs(fsConfig);
+
+ let loaderConfig = {
+ name: 'my-package',
+ path: '../node_modules/my-package'
+ };
+
+ sut.reverseEngineer(loaderConfig)
+ .then(description => {
+ expect(description.source).toBe('npm');
+ expect(description.loaderConfig).toBe(loaderConfig);
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+
+ it('sets source to custom when node_modules is not found in the path', done => {
+ // setup mock package.json
+ const fsConfig = {};
+ fsConfig[path.join('some-folder/my-package', 'package.json')] = '{ }';
+ mockfs(fsConfig);
+
+ let loaderConfig = {
+ name: 'my-package',
+ path: '../some-folder/my-package',
+ packageRoot: '../some-folder/my-package'
+ };
+
+ sut.reverseEngineer(loaderConfig)
+ .then(description => {
+ expect(description.source).toBe('custom');
+ expect(description.loaderConfig).toBe(loaderConfig);
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+
+ it('creates description when there is no package.json', done => {
+ const fsConfig = {};
+ mockfs(fsConfig);
+
+ let loaderConfig = {
+ name: 'my-package',
+ path: '../some-folder/my-package',
+ packageRoot: '../some-folder/my-package'
+ };
+
+ sut.reverseEngineer(loaderConfig)
+ .then(description => {
+ expect(description.source).toBe('custom');
+ expect(description.loaderConfig).toBe(loaderConfig);
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+
+ it('reads package.json as package metadata', done => {
+ // setup mock package.json
+ const fsConfig = {};
+ fsConfig[path.join('some-folder/my-package', 'package.json')] = '{ "name": "my-package" }';
+ mockfs(fsConfig);
+
+ let loaderConfig = {
+ name: 'my-package',
+ path: '../some-folder/my-package',
+ packageRoot: '../some-folder/my-package'
+ };
+
+ sut.reverseEngineer(loaderConfig)
+ .then(description => {
+ expect(description.metadata.name).toBe('my-package');
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+
+ it('analyze() reads package.json as package metadata', done => {
+ // setup mock package.json
+ // setup mock package.json
+ const fsConfig = {};
+ fsConfig[path.join('node_modules/my-package', 'package.json')] = '{ "name": "my-package", "main": "index.js" }';
+ fsConfig[project.paths.root] = {};
+ mockfs(fsConfig);
+
+ sut.analyze('my-package')
+ .then(description => {
+ expect(description.metadata.name).toBe('my-package');
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+
+ it('analyze() determines loaderConfig', done => {
+ // setup mock package.json
+ const fsConfig = {};
+ fsConfig[path.join('node_modules/my-package', 'package.json')] = '{ "name": "my-package", "main": "index.js" }';
+ fsConfig[project.paths.root] = {};
+ mockfs(fsConfig);
+
+ sut.analyze('my-package')
+ .then(description => {
+ expect(description.loaderConfig.name).toBe('my-package');
+ expect(description.loaderConfig.path).toBe('..\\node_modules\\my-package\\index');
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+
+ it('analyze() uses jspm.directories.dist and jspm.main path if available', done => {
+ // setup mock package.json
+ const fsConfig = {};
+ let json = '{ "name": "my-package", "main": "index.js", "jspm": { "directories": { "dist": "foobar" }, "main": "my-main.js" } }';
+ fsConfig[path.join('node_modules/my-package', 'package.json')] = json;
+ fsConfig[project.paths.root] = {};
+ mockfs(fsConfig);
+
+ sut.analyze('my-package')
+ .then(description => {
+ expect(description.loaderConfig.name).toBe('my-package');
+ expect(description.loaderConfig.path).toBe('..\\node_modules\\my-package\\foobar\\my-main');
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+
+ it('analyze() works when there is no package.json. Uses index.js as the main file', done => {
+ // setup mock package.json
+ const fsConfig = {};
+ fsConfig[path.join('node_modules/my-package')] = {};
+ fsConfig[project.paths.root] = {};
+ mockfs(fsConfig);
+
+ sut.analyze('my-package')
+ .then(description => {
+ expect(description.loaderConfig.name).toBe('my-package');
+ expect(description.loaderConfig.path).toBe('..\\node_modules\\my-package\\index');
+ done();
+ })
+ .catch(e => done.fail(e));
+ });
+});

0 comments on commit c225bb7

Please sign in to comment.