Open rrthomas opened 3 years ago
In fact, this should be very easy to achieve, as if I change:
for await (const { cid } of importer([{ content }], block, options)) {
to
for await (const { cid } of importer(content, block, options)) {
then I can pass in the result of e.g. globsource()
.
So it seems that all that is needed to keep things as convenient as at the moment is a bit more automatic type-matching code like the current:
if (typeof content === 'string') {
content = new TextEncoder().encode(content)
}
After a bit more thought, I can see how to detect whether the argument is a Uint8Array
, but not how to distinguish the case where it's an AsyncIterable<Uint8Array>
(where we want to lift it into an array containing an object) from an AsyncIterable<ImportCandidate>
(where we want to leave it alone).
If it's impossible to make this distinction, then it would be possible instead to add a second API to ipfs-only-hash
.
For now, I am using the following code, and specifically the ofDir
method:
// A slightly modified version of ipfs-only-hash
// See https://github.com/alanshaw/ipfs-only-hash/issues/18
const { globSource } = require('ipfs-http-client');
const { importer } = require('ipfs-unixfs-importer');
const block = {
get: async (cid) => { throw new Error(`unexpected block API get for ${cid}`); },
put: async () => { throw new Error('unexpected block API put'); },
};
async function hash(content_, options_) {
const options = options_ || {};
options.onlyHash = true;
let content = content_;
if (typeof content === 'string') {
content = [{ content: new TextEncoder().encode(content) }];
} else if (content instanceof Object.getPrototypeOf(Uint8Array)) {
content = [{ content }];
}
let lastCID;
for await (const { cid } of importer(content, block, options)) {
lastCID = cid;
}
return lastCID;
}
module.exports = {
cidToHex(cid) {
return `0x${Buffer.from(cid.bytes.slice(2)).toString('hex')}`;
},
of: hash,
async ofDir(directory) {
const options = {
cidVersion: 0, // Lines up with the smart contract code
};
const files = globSource(directory, { recursive: true });
const rootCID = await hash(files, options);
return rootCID;
},
};
Updated version of @rrthomas's code. Note ofFile
is distinct to ofDir
, as it doesn't wrap the file in a directory.
Not perfect but good enough to get started with.
// A slightly modified version of ipfs-only-hash
// See https://github.com/alanshaw/ipfs-only-hash/issues/18
const block = {
get: async (cid) => {
throw new Error(`unexpected block API get for ${cid}`);
},
put: async () => {
throw new Error('unexpected block API put');
},
};
async function hash(content_, options_) {
const { importer } = await import('ipfs-unixfs-importer');
const options = options_ || {};
options.onlyHash = true;
let content = content_;
if (typeof content === 'string') {
content = [{ content: new TextEncoder().encode(content) }];
} else if (content instanceof Object.getPrototypeOf(Uint8Array)) {
content = [{ content }];
}
let lastCID;
for await (const c of importer(content, block, options)) {
lastCID = c.cid;
}
return lastCID;
}
async function hashFiles(path, options) {
const { globSource } = await import('ipfs-http-client');
options = {
cidVersion: 0, // Lines up with the smart contract code
hidden: true,
...options,
};
const files = globSource(path, '**');
const rootCID = await hash(files, options);
return rootCID;
}
module.exports = {
cidToHex(cid) {
return `0x${Buffer.from(cid.bytes.slice(2)).toString('hex')}`;
},
of: hash,
async ofFile(path) {
return await hashFiles(path, {});
},
async ofDir(path) {
return await hashFiles(path, {
wrapWithDirectory: true,
});
},
};
@wjagodfrey Thanks for this fix! I will use it myself.
It would be great to be able to use this module to do the equivalent of
ipfs add -r --only-hash
.