Skip to content

Commit c1295d6

Browse files
committed
feat: joinPath()
1 parent b34456a commit c1295d6

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Utilities for [Hexo].
2727
- [hash](#hashstr)
2828
- [highlight](#highlightstr-options)
2929
- [htmlTag](#htmltagtag-attrs-text-escape)
30+
- [joinPath](#joinpathpaths-option)
3031
- [isExternalLink](#isexternallinkurl-sitehost-exclude)
3132
- [Pattern](#patternrule)
3233
- [Permalink](#permalinkrule-options)
@@ -324,6 +325,51 @@ htmlTag('script', {src: '/foo.js', async: true}, '')
324325
// <script src="/foo.js" async></script>
325326
```
326327

328+
### joinPath([...paths][, option])
329+
330+
Join and format paths.
331+
332+
Option | Description | Default
333+
--- | --- | ---
334+
`sep` | Path separator | [`path.sep`](https://nodejs.org/api/path.html#path_path_sep)
335+
336+
``` js
337+
joinPath('foo/bar\\baz')
338+
// Windows: foo\bar\baz
339+
// Unix: foo/bar/baz
340+
341+
joinPath('foo/bar/baz', 'lorem\\ipsum\\dolor')
342+
// Windows: foo\bar\baz\lorem\ipsum\dolor
343+
// Unix: foo/bar/baz/lorem/ipsum/dolor
344+
345+
joinPath('foo/bar/baz', 'lorem\\ipsum\\dolor', { sep: '/' })
346+
// foo/bar/baz/lorem/ipsum/dolor
347+
348+
joinPath('foo/', '/bar', '/baz')
349+
// Windows: foo\bar\baz
350+
// Unix: foo/bar/baz
351+
352+
/* If the joined path string is a zero-length string, then '.' will be returned,
353+
representing the current working directory. */
354+
joinPath()
355+
// .
356+
357+
joinPath('')
358+
// .
359+
```
360+
361+
Platform-specific separator also can be used:
362+
363+
``` js
364+
const { sep } = require('path').win32
365+
joinPath('foo/', '/bar', '/baz', { sep })
366+
// foo\bar\baz
367+
368+
const { sep } = require('path').posix
369+
joinPath('foo/', '/bar', '/baz', { sep })
370+
// foo/bar/baz
371+
```
372+
327373
### isExternalLink(url, sitehost, [exclude])
328374

329375
Option | Description | Default

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ exports.HashStream = hash.HashStream;
2020
exports.highlight = require('./highlight');
2121
exports.htmlTag = require('./html_tag');
2222
exports.isExternalLink = require('./is_external_link');
23+
exports.joinPath = require('./join_path');
2324
exports.Pattern = require('./pattern');
2425
exports.Permalink = require('./permalink');
2526
exports.prettyUrls = require('./pretty_urls');

lib/join_path.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict';
2+
3+
const { sep: sepPlatform } = require('path');
4+
5+
const formatPath = (str, sep) => {
6+
const rDedup = new RegExp('\\' + sep + '{2,}', 'g');
7+
return str.replace(/\/|\\/g, sep).replace(rDedup, sep);
8+
};
9+
10+
const joinPath = (...args) => {
11+
const argsSize = args.length;
12+
13+
// defaults to platform-specific path separator
14+
let sep = sepPlatform;
15+
if (typeof args[argsSize - 1] === 'object') sep = args.pop().sep;
16+
17+
const result = args.join(sep);
18+
19+
// Similar behaviour as path.join()
20+
// https://nodejs.org/api/path.html#path_path_join_paths
21+
if (result.length === 0) return '.';
22+
23+
return formatPath(result, sep);
24+
};
25+
26+
module.exports = joinPath;

test/join_path.spec.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use strict';
2+
3+
require('chai').should();
4+
const { sep } = require('path');
5+
const join = require('../lib/join_path');
6+
7+
describe('joinPath', () => {
8+
it('one path', () => {
9+
const content = 'foo/bar';
10+
join(content).should.eql('foo' + sep + 'bar');
11+
});
12+
13+
it('no argument', () => {
14+
join().should.eql('.');
15+
});
16+
17+
it('zero-length string', () => {
18+
join('').should.eql('.');
19+
});
20+
21+
it('two paths', () => {
22+
join('foo', 'bar').should.eql('foo' + sep + 'bar');
23+
});
24+
25+
it('one path - custom separator', () => {
26+
const custom = '\\';
27+
join('foo/bar', { sep: custom }).should.eql('foo' + custom + 'bar');
28+
});
29+
30+
it('two paths - custom separator', () => {
31+
const custom = '\\';
32+
join('foo', 'bar', { sep: custom }).should.eql('foo' + custom + 'bar');
33+
});
34+
35+
it('deduplicate separators', () => {
36+
join('foo/', '/bar').should.eql('foo' + sep + 'bar');
37+
});
38+
39+
it('starting and ending slashes', () => {
40+
join('/foo/', '/bar/').should.eql(sep + 'foo' + sep + 'bar' + sep);
41+
});
42+
43+
it('mixed slashes', () => {
44+
join('foo/', '\\bar').should.eql('foo' + sep + 'bar');
45+
});
46+
47+
it('mixed and >2 slashes', () => {
48+
join('foo////', '\\\\bar').should.eql('foo' + sep + 'bar');
49+
});
50+
51+
it('mixed and >2 slashes - custom separator', () => {
52+
const custom = '\\';
53+
join('foo////', '\\\\bar', { sep: custom }).should.eql('foo' + custom + 'bar');
54+
});
55+
56+
it('three paths', () => {
57+
join('foo', 'bar', 'baz').should.eql('foo' + sep + 'bar' + sep + 'baz');
58+
});
59+
60+
it('three paths - custom separator', () => {
61+
const custom = '\\';
62+
join('foo', 'bar', 'baz', { sep: custom }).should.eql('foo' + custom + 'bar' + custom + 'baz');
63+
});
64+
});

0 commit comments

Comments
 (0)