Skip to content

Commit

Permalink
Helper '#withPackageOf' adds file-path relative to the project root
Browse files Browse the repository at this point in the history
- Added as "@relativepath"
- Refactorings for when resolving the package.json of a file
- Remove dependency on `find-package` in favour of a custom
  implementation
  • Loading branch information
nknapp committed Jun 10, 2017
1 parent 438b91a commit b52a6fe
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 18 deletions.
43 changes: 29 additions & 14 deletions handlebars/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const _ = {
map: require('lodash.map')
}
const debug = require('debug')('thought:helpers')
const findPackage = require('find-package')
const {resolvePackageRoot} = require('../../lib/utils/resolve-package-root')
const Handlebars = require('handlebars')
const qfs = require('m-io/fs')
const util = require('util')
Expand Down Expand Up @@ -213,18 +213,23 @@ function renderTree (object, options) {
* This block helper executes the block in the current context but sets special variables:
*
* * `@url`: The github-url of the given file in the current package version is stored into
* * `@package`The `package.json` of the file's module is stored into
* * `@package`: The `package.json` of the file's module is stored into
* * `@relativePath`: The relative path of the file within the repository
*
* @param {string} filePath file that is used to find that package.json
* @param {object} options options passed in by Handlebars
* @access public
* @memberOf helpers
*/
function withPackageOf (filePath, options) {
const data = Handlebars.createFrame(options.data)
data.url = github(filePath)
data.package = findPackage(path.resolve(filePath), false)
return options.fn(this, {data: data})
return resolvePackageRoot(path.resolve(filePath))
.then(function (resolvedPackageRoot) {
const data = Handlebars.createFrame(options.data)
data.url = _githubUrl(resolvedPackageRoot)
data.package = resolvedPackageRoot.packageJson
data.relativePath = resolvedPackageRoot.relativeFile
return options.fn(this, {data: data})
})
}

/**
Expand Down Expand Up @@ -382,14 +387,7 @@ function transformTree (object, fn) {
*/
function github (filePath) {
// Build url to correct version and file in githubs
const packageJson = findPackage(path.resolve(filePath), true)
const url = repoWebUrl(packageJson && packageJson.repository && packageJson.repository.url)
if (url && url.match(/github.com/)) {
const version = packageJson.version
// path within the package
const relativePath = path.relative(path.dirname(packageJson.paths.absolute), filePath)
return `${url}/blob/v${version}/${relativePath}`
}
return resolvePackageRoot(path.resolve(filePath)).then(_githubUrl)
}

/**
Expand Down Expand Up @@ -455,3 +453,20 @@ function regex (strings, ...args) {
function arr (...args) {
return args.slice(0, args.length - 1)
}

/**
* Computes the github-url of a file based on the information retrieved by the {@link resolvePackageRoot}
* function. The code is extracted into a function because it is used in multiple places.
* @param {object} resolvedPackageRoot
* @param {object} resolvedPackageRoot.packageJson the contents of the package.json as javascript object
* @param {string} resolvePackageRoot.relativeFile the relative path of the file within the package
* @returns {string}
* @private
*/
function _githubUrl (resolvedPackageRoot) {
var {packageJson, relativeFile} = resolvedPackageRoot
const url = repoWebUrl(packageJson && packageJson.repository && packageJson.repository.url)
if (url && url.match(/github.com/)) {
return `${url}/blob/v${packageJson.version}/${relativeFile}`
}
}
46 changes: 46 additions & 0 deletions lib/utils/resolve-package-root.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const path = require('path')
const fs = require('fs')
const debug = require('debug')('thought:resolve-package')

module.exports = {resolvePackageRoot}

/**
* Find the `package.json` by walking up from a given file.
* The result contains the following properties
*
* * **packageRoot**: The base-directory of the package containing the file (i.e. the parent of the `package.json`
* * **relativeFile**: The path of "file" relative to the packageRoot
* * **packageJson**: The required package.json
*
* @param file
* @return {{packageRoot: string, packageJson: object, relativeFile: string}} the path to the package.json
*/
function resolvePackageRoot (file) {
try {
const fullPath = path.resolve(file)
for (let lead = fullPath; path.dirname(lead) !== lead; lead = path.dirname(lead)) {
debug('Looking for package.json in ' + lead)
let packagePath = path.join(lead, 'package.json')
try {
if (fs.statSync(packagePath).isFile()) {
return Promise.resolve({
packageRoot: path.relative(process.cwd(), lead),
relativeFile: path.relative(lead, fullPath),
packageJson: require(packagePath)
})
}
} catch (e) {
/* istanbul ignore else */
switch (e.code) {
case 'ENOTDIR':
case 'ENOENT':
continue
default:
throw e
}
}
}
} catch (e) {
return Promise.reject(e)
}
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"customize-write-files": "^2.0.0",
"debug": "^2.6.3",
"deep-aplus": "^1.0.4",
"find-package": "^1.0.0",
"glob": "^7.1.1",
"handlebars": "^4.0.6",
"lodash.clonedeep": "^4.5.0",
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/mini-project/a/b/package.json/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
1 change: 1 addition & 0 deletions test/fixtures/mini-project/a/b/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
4 changes: 4 additions & 0 deletions test/fixtures/mini-project/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "mini-project",
"version": "1.0.0"
}
22 changes: 19 additions & 3 deletions test/helper-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ describe('thought-helpers:', function () {
return expectHbs('{{dirTree directory glob ignore=ignore}}', {
directory: 'test/fixtures/dir-tree',
glob: '**',
ignore: [ '**/aFile.txt', '**/bFile.txt' ]
ignore: ['**/aFile.txt', '**/bFile.txt']
})
.to.eventually.equal(fixture('dir-tree.output.ignore.files.txt'))
})
Expand All @@ -148,7 +148,7 @@ describe('thought-helpers:', function () {
it('create links relative to the current target file if the "links"-option is set"', function () {
return expectHbs(
'{{dirTree directory glob links=\'true\'}}',
{ directory: 'test/fixtures/dir-tree/subdirA/bDir' },
{directory: 'test/fixtures/dir-tree/subdirA/bDir'},
'src/test.md'
)
.to.eventually.equal(fixture('dir-tree.output.links.relative.txt'))
Expand Down Expand Up @@ -313,6 +313,22 @@ describe('thought-helpers:', function () {
)
.to.eventually.equal(versions(fixture('include/withPackageOf.no-repo.md')))
})

it('should create a @relativePath for files in dependency projects', function () {
return expectHbs(
'{{#withPackageOf file}}{{@relativePath}}{{/withPackageOf}}',
{file: require.resolve('customize/helpers-io.js')}
)
.to.eventually.equal('helpers-io.js')
})
})

it('should create a @relativePath for projects without repository-property', function () {
return expectHbs(
'{{#withPackageOf file}}{{@relativePath}}{{/withPackageOf}}',
{file: require.resolve('./fixtures/no-git-repo/package.json')}
)
.to.eventually.equal('package.json')
})

describe('The "github"-helper', function () {
Expand Down Expand Up @@ -548,7 +564,7 @@ describe('thought-helpers:', function () {

describe('The "arr" helper', function () {
it('should return an array of its arguments', function () {
return expectHbs("{{#each (arr 'a' 'b' 'c')}}v:{{.}} {{/each}}", {}).to.eventually.equal('v:a v:b v:c')
return expectHbs('{{#each (arr \'a\' \'b\' \'c\')}}v:{{.}} {{/each}}', {}).to.eventually.equal('v:a v:b v:c')
})
})

Expand Down
52 changes: 52 additions & 0 deletions test/resolve-package-root-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*!
* thought <https://github.com/nknapp/thought>
*
* Copyright (c) 2015 Nils Knappmeier.
* Released under the MIT license.
*/

/* eslint-env mocha */

'use strict'

var chai = require('chai')
chai.use(require('chai-as-promised'))
chai.use(require('dirty-chai'))
var expect = chai.expect
var {resolvePackageRoot} = require('../lib/utils/resolve-package-root')

describe('The "resolve-package-root" utility', function () {
var statSync
var fs = require('fs')

beforeEach(function () {
statSync = fs.statSync
})

afterEach(function () {
fs.statSync = statSync
})

it('find a package.json file and provide the relative path of the given file', function () {
return expect(resolvePackageRoot('test/fixtures/mini-project/a/b/test.txt')).to.eventually.deep.equal({
packageRoot: 'test/fixtures/mini-project',
relativeFile: 'a/b/test.txt',
packageJson: {'name': 'mini-project', 'version': '1.0.0'}
})
})

it('find not be bothered by directory named "package.json"', function () {
return expect(resolvePackageRoot('test/fixtures/mini-project/a/b/package.json/test.txt')).to.eventually.deep.equal({
packageRoot: 'test/fixtures/mini-project',
relativeFile: 'a/b/package.json/test.txt',
packageJson: {'name': 'mini-project', 'version': '1.0.0'}
})
})

it('should return a rejected promise on unexpected exceptions', function () {
fs.statSync = function () {
throw new Error('Test-Error')
}
return expect(resolvePackageRoot('test/fixtures/mini-project/a/b/package.json/test.txt')).to.be.rejectedWith('Test-Error')
})
})

0 comments on commit b52a6fe

Please sign in to comment.