diff --git a/src/cli/commands/ls.js b/src/cli/commands/ls.js index cbac9a049c..c79eaeee04 100644 --- a/src/cli/commands/ls.js +++ b/src/cli/commands/ls.js @@ -48,6 +48,14 @@ module.exports = { const multihashWidth = Math.max.apply(null, links.map((file) => file.hash.length)) const sizeWidth = Math.max.apply(null, links.map((file) => String(file.size).length)) + // replace multiple slashes + key = key.replace(/\/(\/+)/g, '/') + + // strip trailing flash + if (key.endsWith('/')) { + key = key.replace(/(\/+)$/, '') + } + let pathParts = key.split('/') if (key.startsWith('/ipfs/')) { @@ -56,7 +64,10 @@ module.exports = { links.forEach(link => { const fileName = link.type === 'dir' ? `${link.name || ''}/` : link.name - const padding = link.depth - pathParts.length + + // todo: fix this by resolving https://github.com/ipfs/js-ipfs-unixfs-exporter/issues/24 + const padding = Math.max(link.depth - pathParts.length, 0) + print( rightpad(link.hash, multihashWidth + 1) + rightpad(link.size || '-', sizeWidth + 1) + diff --git a/test/cli/ls.js b/test/cli/ls.js index bd5870c6fa..dc4d1de661 100644 --- a/test/cli/ls.js +++ b/test/cli/ls.js @@ -25,6 +25,54 @@ describe('ls', () => runOnAndOff((thing) => { ) }) + it('supports a trailing slash', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z/') + expect(out).to.eql( + 'QmamKEPmEH9RUsqRQsfNf5evZQDQPYL9KXg1ADeT7mkHkT - blocks/\n' + + 'QmPkWYfSLCEBLZu7BZt4kigGDMe3cpogMbeVf97gN2xJDN 3928 config\n' + + 'QmUqyZtPmsRy1U5Mo8kz2BAMmk1hfJ7yW1KAFTMB2odsFv - datastore/\n' + + 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU - init-docs/\n' + + 'QmR56UJmAaZLXLdTT1ALrE9vVqV8soUEekm9BMd4FnuYqV 2 version\n' + ) + }) + + it('supports multiple trailing slashes', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z///') + expect(out).to.eql( + 'QmamKEPmEH9RUsqRQsfNf5evZQDQPYL9KXg1ADeT7mkHkT - blocks/\n' + + 'QmPkWYfSLCEBLZu7BZt4kigGDMe3cpogMbeVf97gN2xJDN 3928 config\n' + + 'QmUqyZtPmsRy1U5Mo8kz2BAMmk1hfJ7yW1KAFTMB2odsFv - datastore/\n' + + 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU - init-docs/\n' + + 'QmR56UJmAaZLXLdTT1ALrE9vVqV8soUEekm9BMd4FnuYqV 2 version\n' + ) + }) + + it('supports multiple intermediate slashes', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z///init-docs') + expect(out).to.eql( + 'QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V 1677 about\n' + + 'QmYCvbfNbCwFR45HiNP45rwJgvatpiW38D961L5qAhUM5Y 189 contact\n' + + 'QmegvLXxpVKiZ4b57Xs1syfBVRd8CbucVHAp7KpLQdGieC - docs/\n' + + 'QmY5heUM5qgRubMDD1og9fhCPA6QdkMp3QCwd4s7gJsyE7 311 help\n' + + 'QmdncfsVm2h5Kqq9hPmU7oAVX2zTSVP3L869tgTbPYnsha 1717 quick-start\n' + + 'QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB 1091 readme\n' + + 'QmTumTjvcYCAvRRwQ8sDRxh8ezmrcr88YFU7iYNroGGTBZ 1016 security-notes\n' + + 'QmciSU8hfpAXKjvK5YLUSwApomGSWN5gFbP4EpDAEzu2Te - tour/\n' + ) + }) + + it('supports recursive listing through intermediate directories', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls -r Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z/blocks/CIQLB') + expect(out).to.eql( + 'QmQ8ag7ysVyCMzJGFjxrUStwWtniQ69c7G9aezbmsKeNYD 10849 CIQLBK52T5EHVHZY5URTG5JS3JCUJDQM2DRB5RVF33DCUUOFJNGVDUI.data\n' + + 'QmaSjzSSRanYzRGPXQY6m5SWfSkkfcnzNkurJEQc4chPJx 10807 CIQLBS5HG4PRCRQ7O4EBXFD5QN6MTI5YBYMCVQJDXPKCOVR6RMLHZFQ.data\n' + ) + }) + it('prints nothing for non-existant hashes', async function () { if (thing.on) { // If the daemon is on, ls should search until it hits a timeout