diff --git a/.eslintrc b/.eslintrc index 88e5a82..f0d5013 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,6 @@ { - "extends": "ipfs" -} \ No newline at end of file + "extends": "ipfs", + "parserOptions": { + "sourceType": "module" + } +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3619d39..8506d63 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [14.x, 16.x] + node-version: [16.x, 18.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} diff --git a/md/api.md b/md/api.md index 1ab06fa..9ac2046 100644 --- a/md/api.md +++ b/md/api.md @@ -3,8 +3,10 @@ We've been trying to make `ipfs-deploy` more friendly as a library. However, we have no documentation yet. +This library is only available in ESM format. + ```javascript -const { deploy, dnsLinkers, dnsLinkersMap, pinners, pinnersMap} = require('ipfs-deploy') +import { deploy, dnsLinkers, dnsLinkersMap, pinners, pinnersMap } from 'ipfs-deploy' // Get available dnsLinkers identifiers dnsLinkersMap.keys() @@ -21,7 +23,7 @@ dnsLinkersMap.get('cloudflare') How we currently use the deploy function: ```javascript -const { deploy } = require('ipfs-deploy') +import { deploy } from 'ipfs-deploy' const cid = await deploy({ dir: argv.path, diff --git a/md/contributing.md b/md/contributing.md index 86ea12d..26e73dd 100644 --- a/md/contributing.md +++ b/md/contributing.md @@ -25,9 +25,7 @@ with the name of the pinning service. Let's say it's called `PinningService`: create a file at `src/pinners/pinning-service.js` with the following contents: ```javascript -'use strict' - -class PinningService { +export default class PinningService { constructor () { // TODO } @@ -52,8 +50,6 @@ class PinningService { return 'pinning-service' } } - -module.exports = PinningService ``` Where `options` in the constructor are the required parameters to connect to @@ -69,7 +65,7 @@ the name of the DNS provider. Let's say it's called `DNS Provider`: create a file at `src/dnslinkers/dns-provider.js` with the following contents: ```javascript -class DNSProvider { +export default class DNSProvider { constructor () { // TODO } @@ -94,9 +90,6 @@ class DNSProvider { return 'dns-provider' } } - -module.exports = DNSProvider - ``` Where `options` in the constructor are the required parameters to connect to @@ -104,4 +97,4 @@ such provider. You will also need to add the provider to the list `dnsLinkers` at `src/dnslinkers/index.js`, as well as adding the required options in `src/cli.js`. -Also, do not forget to add documentation! \ No newline at end of file +Also, do not forget to add documentation! diff --git a/package.json b/package.json index c881419..8192d14 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,15 @@ "src", "dist" ], + "type": "module", "main": "src/index.js", "types": "dist/src/index.d.ts", + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "import": "./src/index.js" + } + }, "typesVersions": { "*": { "src/*": [ @@ -44,8 +51,8 @@ "url": "https://github.com/ipfs-shipyard/ipfs-deploy.git" }, "scripts": { - "lint": "aegir lint && aegir ts -p check", - "test": "aegir test", + "lint": "aegir lint", + "test": "aegir test -t node -- --loader=esmock", "types": "aegir ts -p types", "release": "aegir release -t node", "release-minor": "aegir release --type minor", @@ -67,8 +74,8 @@ ], "dependencies": { "@aws-sdk/client-route-53": "^3.53.0", - "@filebase/client": "^0.0.2", - "axios": "^0.26.0", + "@filebase/client": "^0.0.4", + "axios": "^1.2.6", "byte-size": "^8.1.0", "chalk": "^4.1.1", "clipboardy": "^2.3.0", @@ -78,7 +85,7 @@ "dreamhost": "^1.0.5", "files-from-path": "^0.2.6", "form-data": "^4.0.0", - "ipfs-http-client": "^50.1.0", + "ipfs-http-client": "^60.0.0", "it-all": "^1.0.6", "lodash.isempty": "^4.4.0", "lodash.isstring": "^4.0.1", @@ -91,12 +98,11 @@ "devDependencies": { "@types/lodash.isempty": "^4.4.6", "@types/lodash.isstring": "^4.0.1", - "@types/proxyquire": "^1.3.28", - "aegir": "^33.2.0", - "proxyquire": "^2.1.3" + "aegir": "^38.1.0", + "esmock": "^2.1.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" }, "contributors": [ "Henrique Dias ", diff --git a/src/cli.js b/src/cli.js index d7a6cdb..bf862d7 100755 --- a/src/cli.js +++ b/src/cli.js @@ -3,14 +3,17 @@ // @ts-nocheck /* eslint-disable no-console */ -'use strict' -const updateNotifier = require('update-notifier') -const yargs = require('yargs') -const dotenv = require('dotenv') +import { readFile } from 'node:fs/promises' +import updateNotifier from 'update-notifier' +import yargs from 'yargs/yargs' +import { hideBin } from 'yargs/helpers' +import dotenv from 'dotenv' -const { deploy, dnsLinkersMap, pinnersMap } = require('.') -const pkg = require('../package.json') +import { deploy, dnsLinkersMap, pinnersMap } from './index.js' + +const pkgUrl = new URL('../package.json', import.meta.url) +const pkg = JSON.parse(await readFile(pkgUrl, 'utf8')) const dnsProviders = [...dnsLinkersMap.keys()] const pinningServices = [...pinnersMap.keys()] @@ -18,7 +21,7 @@ const pinningServices = [...pinnersMap.keys()] updateNotifier({ pkg, updateCheckInterval: 0 }).notify() dotenv.config() -const argv = yargs +const argv = yargs(hideBin(process.argv)) .scriptName('ipfs-deploy') .usage( '$0 [path] [options]', diff --git a/src/deploy.js b/src/deploy.js index fead1f4..169b4a5 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,19 +1,18 @@ -'use strict' +import { fileURLToPath } from 'node:url' +import chalk from 'chalk' +import path from 'node:path' +import fs from 'node:fs' -const chalk = require('chalk') -const path = require('path') -const fs = require('fs') - -const { dnsLinkersMap } = require('./dnslinkers') -const { pinnersMap } = require('./pinners') -const { guessPath, getReadableSize, terminalUrl } = require('./utils') +import { dnsLinkersMap } from './dnslinkers/index.js' +import { pinnersMap } from './pinners/index.js' +import { guessPath, getReadableSize, terminalUrl } from './utils.js' /** - * @typedef {import('./dnslinkers/types').DNSLinker} DNSLinker - * @typedef {import('./pinners/types').PinningService} PinningService - * @typedef {import('./pinners/types').PinDirOptions} PinDirOptions - * @typedef {import('./types').DeployOptions} DeployOptions - * @typedef {import('./types').Logger} Logger + * @typedef {import('./dnslinkers/types.js').DNSLinker} DNSLinker + * @typedef {import('./pinners/types.js').PinningService} PinningService + * @typedef {import('./pinners/types.js').PinDirOptions} PinDirOptions + * @typedef {import('./types.js').DeployOptions} DeployOptions + * @typedef {import('./types.js').Logger} Logger */ /** @@ -96,7 +95,7 @@ async function dnsLink (services, cid, logger) { * @param {string[]} gatewayUrls * @param {Logger} logger */ -function copyToClipboard (hostnames, gatewayUrls, logger) { +async function copyToClipboard (hostnames, gatewayUrls, logger) { let toCopy if (hostnames.length > 0) { toCopy = hostnames[hostnames.length - 1] @@ -111,11 +110,11 @@ function copyToClipboard (hostnames, gatewayUrls, logger) { } try { - const clipboardy = require('clipboardy') - clipboardy.writeSync(toCopy) + const { default: clipboardy } = await import('clipboardy') + await clipboardy.write(toCopy) logger.info('📋 Copied HTTP gateway URL to clipboard:') logger.info(terminalUrl(toCopy, toCopy)) - } catch (e) { + } catch (/** @type {any} */ e) { logger.info('⚠️ Could not copy URL to clipboard.') logger.error(e.stack || e.toString()) } @@ -130,15 +129,15 @@ function copyToClipboard (hostnames, gatewayUrls, logger) { * @param {string[]} hostnames * @param {Logger} logger */ -function openUrlsBrowser (gatewayUrls, hostnames, logger) { +async function openUrlsBrowser (gatewayUrls, hostnames, logger) { logger.info('🏄 Opening URLs on web browser...') try { - const open = require('open') + const { default: open } = await import('open') gatewayUrls.forEach(url => { open(url) }) hostnames.forEach(hostname => open(`https://${hostname}`)) logger.info('🏄 All URLs opened.') - } catch (e) { + } catch (/** @type {any} */ e) { logger.info('⚠️ Could not open URLs on web browser.') logger.error(e.stack || e.toString()) } @@ -189,7 +188,7 @@ async function checkDirAndCid (dir, cid, logger) { * @param {DeployOptions} options * @returns {Promise} */ -async function deploy ({ +export default async function deploy ({ dir, cid, tag, @@ -211,7 +210,7 @@ async function deploy ({ dir = res.dir cid = res.cid - tag = tag || __dirname + tag = tag || fileURLToPath(new URL('.', import.meta.url)) // In the case we only set pinning services and we're deploying a directory, // then call those upload services. @@ -270,15 +269,13 @@ async function deploy ({ const hostnames = await dnsLink(dnsProviders, cid, logger) if (openUrls) { - openUrlsBrowser(gatewayUrls, hostnames, logger) + await openUrlsBrowser(gatewayUrls, hostnames, logger) } if (copyUrl) { - copyToClipboard(hostnames, gatewayUrls, logger) + await copyToClipboard(hostnames, gatewayUrls, logger) } logger.out(cid) return cid } - -module.exports = deploy diff --git a/src/dnslinkers/cloudflare.js b/src/dnslinkers/cloudflare.js index bc9700e..161fd55 100644 --- a/src/dnslinkers/cloudflare.js +++ b/src/dnslinkers/cloudflare.js @@ -1,15 +1,13 @@ -'use strict' - // @ts-ignore -const dnslink = require('dnslink-cloudflare') -const isEmpty = require('lodash.isempty') +import dnslink from 'dnslink-cloudflare' +import isEmpty from 'lodash.isempty' /** - * @typedef {import('./types').DNSRecord} DNSRecord - * @typedef {import('./types').CloudflareOptions} CloudflareOptions + * @typedef {import('./types.js').DNSRecord} DNSRecord + * @typedef {import('./types.js').CloudflareOptions} CloudflareOptions */ -class Cloudflare { +export default class Cloudflare { /** * @param {CloudflareOptions} options */ @@ -64,5 +62,3 @@ class Cloudflare { return 'cloudflare' } } - -module.exports = Cloudflare diff --git a/src/dnslinkers/dnsimple.js b/src/dnslinkers/dnsimple.js index 1d8282f..2881a70 100644 --- a/src/dnslinkers/dnsimple.js +++ b/src/dnslinkers/dnsimple.js @@ -1,15 +1,13 @@ -'use strict' - // @ts-ignore -const dnslink = require('dnslink-dnsimple') -const isEmpty = require('lodash.isempty') +import dnslink from 'dnslink-dnsimple' +import isEmpty from 'lodash.isempty' /** - * @typedef {import('./types').DNSRecord} DNSRecord - * @typedef {import('./types').DNSimpleOptions} DNSimpleOptions + * @typedef {import('./types.js').DNSRecord} DNSRecord + * @typedef {import('./types.js').DNSimpleOptions} DNSimpleOptions */ -class DNSimple { +export default class DNSimple { /** * @param {DNSimpleOptions} options */ @@ -63,5 +61,3 @@ class DNSimple { return 'dnsimple' } } - -module.exports = DNSimple diff --git a/src/dnslinkers/dreamhost.js b/src/dnslinkers/dreamhost.js index 4dfeba3..56210f6 100644 --- a/src/dnslinkers/dreamhost.js +++ b/src/dnslinkers/dreamhost.js @@ -1,15 +1,13 @@ -'use strict' - // @ts-ignore -const DreamHostClient = require('dreamhost') -const isEmpty = require('lodash.isempty') +import DreamHostClient from 'dreamhost' +import isEmpty from 'lodash.isempty' /** - * @typedef {import('./types').DNSRecord} DNSRecord - * @typedef {import('./types').DreamHostOptions} DreamHostOptions + * @typedef {import('./types.js').DNSRecord} DNSRecord + * @typedef {import('./types.js').DreamHostOptions} DreamHostOptions */ -class DreamHost { +export default class DreamHost { /** * @param {DreamHostOptions} options */ @@ -80,5 +78,3 @@ class DreamHost { return 'dreamhost' } } - -module.exports = DreamHost diff --git a/src/dnslinkers/index.js b/src/dnslinkers/index.js index 081f633..151a894 100644 --- a/src/dnslinkers/index.js +++ b/src/dnslinkers/index.js @@ -1,18 +1,11 @@ -'use strict' +import Cloudflare from './cloudflare.js' +import DNSimple from './dnsimple.js' +import DreamHost from './dreamhost.js' +import Route53 from './route53.js' -const Cloudflare = require('./cloudflare') -const DNSimple = require('./dnsimple') -const DreamHost = require('./dreamhost') -const Route53 = require('./route53') +export const dnsLinkers = [Cloudflare, DNSimple, DreamHost, Route53] -const dnsLinkers = [Cloudflare, DNSimple, DreamHost, Route53] - -const dnsLinkersMap = dnsLinkers.reduce((map, dnsLinker) => { +export const dnsLinkersMap = dnsLinkers.reduce((map, dnsLinker) => { map.set(dnsLinker.slug, dnsLinker) return map }, new Map()) - -module.exports = { - dnsLinkers, - dnsLinkersMap -} diff --git a/src/dnslinkers/route53.js b/src/dnslinkers/route53.js index fac7abe..0a845e8 100644 --- a/src/dnslinkers/route53.js +++ b/src/dnslinkers/route53.js @@ -1,17 +1,15 @@ -'use strict' - // @ts-ignore -const { Route53Client, ChangeResourceRecordSetsCommand } = require('@aws-sdk/client-route-53') -const isEmpty = require('lodash.isempty') +import { Route53Client, ChangeResourceRecordSetsCommand } from '@aws-sdk/client-route-53' +import isEmpty from 'lodash.isempty' const TTL = 60 /** - * @typedef {import('./types').DNSRecord} DNSRecord - * @typedef {import('./types').Route53Options} Route53Options + * @typedef {import('./types.js').DNSRecord} DNSRecord + * @typedef {import('./types.js').Route53Options} Route53Options */ -class Route53 { +export default class Route53 { /** * @param {Route53Options} options */ @@ -48,7 +46,7 @@ class Route53 { ResourceRecordSet: { Name: this.record, Type: 'TXT', - TTL: TTL, + TTL, ResourceRecords: [ { Value: `"${txtValue}"` @@ -80,5 +78,3 @@ class Route53 { return 'route53' } } - -module.exports = Route53 diff --git a/src/index.js b/src/index.js index 7d414c2..c0674d8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,3 @@ -'use strict' - -const { dnsLinkers, dnsLinkersMap } = require('./dnslinkers') -const { pinners, pinnersMap } = require('./pinners') -const deploy = require('./deploy') - -module.exports = { - deploy, - dnsLinkers, - dnsLinkersMap, - pinners, - pinnersMap -} +export { dnsLinkers, dnsLinkersMap } from './dnslinkers/index.js' +export { pinners, pinnersMap } from './pinners/index.js' +export { default as deploy } from './deploy.js' diff --git a/src/pinners/c4rex.js b/src/pinners/c4rex.js index 0a56feb..fba0079 100644 --- a/src/pinners/c4rex.js +++ b/src/pinners/c4rex.js @@ -1,8 +1,6 @@ -'use strict' +import IpfsNode from './ipfs-node.js' -const IpfsNode = require('./ipfs-node') - -class c4rex extends IpfsNode { +export default class c4rex extends IpfsNode { constructor () { super({ host: 'api.ipfs.c4rex.co', @@ -31,5 +29,3 @@ class c4rex extends IpfsNode { return 'c4rex' } } - -module.exports = c4rex diff --git a/src/pinners/dappnode.js b/src/pinners/dappnode.js index 8ddbfda..154bb4e 100644 --- a/src/pinners/dappnode.js +++ b/src/pinners/dappnode.js @@ -1,8 +1,6 @@ -'use strict' +import IpfsNode from './ipfs-node.js' -const IpfsNode = require('./ipfs-node') - -class DAppNode extends IpfsNode { +export default class DAppNode extends IpfsNode { constructor () { super({ host: 'ipfs.dappnode', @@ -23,5 +21,3 @@ class DAppNode extends IpfsNode { return 'dappnode' } } - -module.exports = DAppNode diff --git a/src/pinners/filebase.js b/src/pinners/filebase.js index 8bbeafb..93d903b 100644 --- a/src/pinners/filebase.js +++ b/src/pinners/filebase.js @@ -1,18 +1,18 @@ -'use strict' - -const isEmpty = require('lodash.isempty') -const { FilebaseClient } = require('@filebase/client') -const { filesFromPath } = require('files-from-path') -const { default: axios } = require('axios') +import isEmpty from 'lodash.isempty' +// @ts-ignore +import { FilebaseClient } from '@filebase/client' +// @ts-ignore +import { filesFromPath } from 'files-from-path' +import axios from 'axios' /** - * @typedef {import('./types').FilebaseOptions} FilebaseOptions - * @typedef {import('./types').PinDirOptions} PinDirOptions + * @typedef {import('./types.js').FilebaseOptions} FilebaseOptions + * @typedef {import('./types.js').PinDirOptions} PinDirOptions */ const PIN_HASH_URL = 'https://api.filebase.io/v1/ipfs/pins' -class Filebase { +export default class Filebase { /** * @param {FilebaseOptions} options */ @@ -24,7 +24,7 @@ class Filebase { this.auth = { api_key: apiKey, secret_api_key: secretApiKey, - bucket: bucket + bucket } this.tokenString = btoa(`${this.auth.api_key}:${this.auth.secret_api_key}:${this.auth.bucket}`) @@ -57,7 +57,7 @@ class Filebase { */ async pinCid (cid, tag) { const body = JSON.stringify({ - cid: cid, + cid, name: tag }) @@ -91,5 +91,3 @@ class Filebase { return 'filebase' } } - -module.exports = Filebase diff --git a/src/pinners/index.js b/src/pinners/index.js index 0f7138b..3704053 100644 --- a/src/pinners/index.js +++ b/src/pinners/index.js @@ -1,13 +1,11 @@ -'use strict' +import DAppNode from './dappnode.js' +import Infura from './infura.js' +import IpfsCluster from './ipfs-cluster.js' +import Pinata from './pinata.js' +import c4rex from './c4rex.js' +import Filebase from './filebase.js' -const DAppNode = require('./dappnode') -const Infura = require('./infura') -const IpfsCluster = require('./ipfs-cluster') -const Pinata = require('./pinata') -const c4rex = require('./c4rex') -const Filebase = require('./filebase') - -const pinners = [ +export const pinners = [ DAppNode, Infura, IpfsCluster, @@ -16,12 +14,7 @@ const pinners = [ Filebase ] -const pinnersMap = pinners.reduce((map, pinner) => { +export const pinnersMap = pinners.reduce((map, pinner) => { map.set(pinner.slug, pinner) return map }, new Map()) - -module.exports = { - pinners, - pinnersMap -} diff --git a/src/pinners/infura.js b/src/pinners/infura.js index 7568f20..5e0caa7 100644 --- a/src/pinners/infura.js +++ b/src/pinners/infura.js @@ -1,14 +1,12 @@ -'use strict' - -const isString = require('lodash.isstring') -const IpfsNode = require('./ipfs-node') +import isString from 'lodash.isstring' +import IpfsNode from './ipfs-node.js' /** * @typedef {import('ipfs-http-client').Options} IpfsOptions - * @typedef {import('./types').InfuraOptions} InfuraOptions + * @typedef {import('./types.js').InfuraOptions} InfuraOptions */ -class Infura extends IpfsNode { +export default class Infura extends IpfsNode { /** * @param {InfuraOptions} options */ @@ -46,5 +44,3 @@ class Infura extends IpfsNode { return 'infura' } } - -module.exports = Infura diff --git a/src/pinners/ipfs-cluster.js b/src/pinners/ipfs-cluster.js index 55682c0..f2bd02e 100644 --- a/src/pinners/ipfs-cluster.js +++ b/src/pinners/ipfs-cluster.js @@ -1,16 +1,13 @@ -'use strict' - -const { default: axios } = require('axios') -const path = require('path') -const isEmpty = require('lodash.isempty') -const { getDirFormData } = require('./utils') +import axios from 'axios' +import isEmpty from 'lodash.isempty' +import { getDirFormData } from './utils.js' /** - * @typedef {import('./types').IPFSClusterOptions} IPFSClusterOptions - * @typedef {import('./types').PinDirOptions} PinDirOptions + * @typedef {import('./types.js').IPFSClusterOptions} IPFSClusterOptions + * @typedef {import('./types.js').PinDirOptions} PinDirOptions */ -class IpfsCluster { +export default class IpfsCluster { /** * * @param {IPFSClusterOptions} options @@ -32,7 +29,7 @@ class IpfsCluster { * @returns {Promise} */ async pinDir (dir, { tag, hidden = false } = {}) { - const data = await getDirFormData(dir, hidden) + const data = await getDirFormData(dir, hidden, 'upload') const res = await axios .post(`${this.host}/add?name=${tag}`, data, { @@ -50,9 +47,8 @@ class IpfsCluster { .split('\n') .map(JSON.parse) - const basename = path.basename(dir) // @ts-ignore - const root = results.find(({ name }) => name === basename) + const root = results.find(({ name }) => name === 'upload') if (!root) { throw new Error('could not determine the CID') @@ -93,5 +89,3 @@ class IpfsCluster { return 'ipfs-cluster' } } - -module.exports = IpfsCluster diff --git a/src/pinners/ipfs-node.js b/src/pinners/ipfs-node.js index 364ce96..02c8349 100644 --- a/src/pinners/ipfs-node.js +++ b/src/pinners/ipfs-node.js @@ -1,15 +1,12 @@ -'use strict' - -const { create: ipfsHttp, globSource } = require('ipfs-http-client') -const all = require('it-all') -const path = require('path') +import { create as ipfsHttp, globSource } from 'ipfs-http-client' +import all from 'it-all' /** * @typedef {import('ipfs-http-client').Options} IpfsOptions - * @typedef {import('./types').PinDirOptions} PinDirOptions + * @typedef {import('./types.js').PinDirOptions} PinDirOptions */ -class IpfsNode { +export default class IpfsNode { /** * @param {IpfsOptions} options */ @@ -23,9 +20,17 @@ class IpfsNode { * @returns {Promise} */ async pinDir (dir, { tag, hidden = false } = {}) { - const response = await all(this.ipfs.addAll(globSource(dir, { recursive: true, hidden }))) - const basename = path.basename(dir) - const root = response.find(({ path }) => path === basename) + const response = await all(this.ipfs.addAll( + (async function * () { + for await (const file of globSource(dir, '**/*', { hidden })) { + yield { + ...file, + path: 'upload' + file.path + } + } + })() + )) + const root = response.find(({ path }) => path === 'upload') if (!root) { throw new Error('could not determine the CID') @@ -63,5 +68,3 @@ class IpfsNode { return 'ipfs-node' } } - -module.exports = IpfsNode diff --git a/src/pinners/pinata.js b/src/pinners/pinata.js index 66ab698..a52f0df 100644 --- a/src/pinners/pinata.js +++ b/src/pinners/pinata.js @@ -1,12 +1,10 @@ -'use strict' - -const { default: axios } = require('axios') -const isEmpty = require('lodash.isempty') -const { getDirFormData } = require('./utils') +import axios from 'axios' +import isEmpty from 'lodash.isempty' +import { getDirFormData } from './utils.js' /** - * @typedef {import('./types').PinataOptions} PinataOptions - * @typedef {import('./types').PinDirOptions} PinDirOptions + * @typedef {import('./types.js').PinataOptions} PinataOptions + * @typedef {import('./types.js').PinDirOptions} PinDirOptions */ const MAX_RETRIES = 3 @@ -14,7 +12,7 @@ const RETRY_CODES = [429] const PIN_DIR_URL = 'https://api.pinata.cloud/pinning/pinFileToIPFS' const PIN_HASH_URL = 'https://api.pinata.cloud/pinning/pinByHash' -class Pinata { +export default class Pinata { /** * @param {PinataOptions} options */ @@ -55,7 +53,7 @@ class Pinata { }) return res.data.IpfsHash - } catch (err) { + } catch (/** @type {any} */ err) { if (err && err.response && RETRY_CODES.includes(err.response.status)) { retries += 1 lastErrorCode = err.response.status @@ -111,5 +109,3 @@ class Pinata { return 'pinata' } } - -module.exports = Pinata diff --git a/src/pinners/types.ts b/src/pinners/types.ts index 5e5f78b..75564e2 100644 --- a/src/pinners/types.ts +++ b/src/pinners/types.ts @@ -4,8 +4,8 @@ export interface PinDirOptions { } export interface PinningService { - pinDir: (dir: string, options: PinDirOptions|undefined) => Promise - pinCid: (cid: string, tag: string|undefined) => void + pinDir: (dir: string, options: PinDirOptions | undefined) => Promise + pinCid: (cid: string, tag: string | undefined) => void gatewayUrl: (cid: string) => string displayName: string } diff --git a/src/pinners/utils.js b/src/pinners/utils.js index 48a3197..50ff7df 100644 --- a/src/pinners/utils.js +++ b/src/pinners/utils.js @@ -1,30 +1,24 @@ -'use strict' - -const FormData = require('form-data') -const path = require('path') -const { globSource } = require('ipfs-http-client') +import FormData from 'form-data' +import { globSource } from 'ipfs-http-client' /** * @param {string} dir * @param {boolean} hidden + * @param {string} pathPrefix * @returns {Promise} */ -async function getDirFormData (dir, hidden) { +export async function getDirFormData (dir, hidden, pathPrefix = '') { const data = new FormData() - for await (const file of globSource(dir, { recursive: true, hidden })) { + for await (const file of globSource(dir, '**/*', { hidden })) { // @ts-ignore if (file.content) { // @ts-ignore data.append('file', file.content, { - filepath: path.normalize(file.path) + filepath: pathPrefix + file.path }) } } return data } - -module.exports = { - getDirFormData -} diff --git a/src/utils.js b/src/utils.js index 34c97a8..d016eb5 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,12 +1,10 @@ -'use strict' - -const { existsSync } = require('fs') +import { existsSync } from 'node:fs' // @ts-ignore -const trammel = require('trammel') +import trammel from 'trammel' // @ts-ignore -const byteSize = require('byte-size') -const terminalLink = require('terminal-link') -const chalk = require('chalk') +import byteSize from 'byte-size' +import terminalLink from 'terminal-link' +import chalk from 'chalk' const pathsToCheck = [ '_site', // jekyll, hakyll, eleventy @@ -25,7 +23,7 @@ const pathsToCheck = [ * * @returns {string} */ -function guessPath () { +export function guessPath () { const guesses = pathsToCheck.filter(existsSync) if (guesses.length > 1) { throw new Error('more than one guessable path to deploy, please specify a path') @@ -44,7 +42,7 @@ function guessPath () { * @param {string} path * @returns {Promise} */ -async function getReadableSize (path) { +export async function getReadableSize (path) { const size = await trammel(path, { stopOnError: true, type: 'raw' @@ -62,12 +60,6 @@ async function getReadableSize (path) { * @param {string} link * @returns {string} */ -function terminalUrl (title, link) { +export function terminalUrl (title, link) { return `🔗 ${chalk.green(terminalLink(title, link))}` } - -module.exports = { - guessPath, - getReadableSize, - terminalUrl -} diff --git a/test/dnslinkers/cloudflare.spec.js b/test/dnslinkers/cloudflare.spec.js index b54d166..875205e 100644 --- a/test/dnslinkers/cloudflare.spec.js +++ b/test/dnslinkers/cloudflare.spec.js @@ -1,11 +1,10 @@ /* eslint max-nested-callbacks: ["error", 8] */ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const proxyquire = require('proxyquire').noCallThru() +import { expect } from 'aegir/chai' +import esmock from 'esmock' -const Cloudflare = proxyquire('../../src/dnslinkers/cloudflare', { +const Cloudflare = await esmock('../../src/dnslinkers/cloudflare.js', { 'dnslink-cloudflare': () => 'value' }) diff --git a/test/dnslinkers/dnsimple.spec.js b/test/dnslinkers/dnsimple.spec.js index 63638e0..42f5a08 100644 --- a/test/dnslinkers/dnsimple.spec.js +++ b/test/dnslinkers/dnsimple.spec.js @@ -1,11 +1,10 @@ /* eslint max-nested-callbacks: ["error", 8] */ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const proxyquire = require('proxyquire').noCallThru() +import { expect } from 'aegir/chai' +import esmock from 'esmock' -const DNSimple = proxyquire('../../src/dnslinkers/dnsimple', { +const DNSimple = await esmock('../../src/dnslinkers/dnsimple.js', { // @ts-ignore 'dnslink-dnsimple': (_token, { link }) => { return { record: { content: `dnslink=${link}` } } diff --git a/test/pinners/ipfs-cluster.spec.js b/test/pinners/ipfs-cluster.spec.js index 2903871..ff2279f 100644 --- a/test/pinners/ipfs-cluster.spec.js +++ b/test/pinners/ipfs-cluster.spec.js @@ -1,11 +1,10 @@ /* eslint max-nested-callbacks: ["error", 8] */ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const proxyquire = require('proxyquire').noCallThru() +import { expect } from 'aegir/chai' +import esmock from 'esmock' -const IpfsCluster = proxyquire('../../src/pinners/ipfs-cluster', { +const IpfsCluster = await esmock('../../src/pinners/ipfs-cluster', { axios: { default: { post: async () => ({ diff --git a/test/pinners/pinata.spec.js b/test/pinners/pinata.spec.js index 8bc2491..0d98e9d 100644 --- a/test/pinners/pinata.spec.js +++ b/test/pinners/pinata.spec.js @@ -1,9 +1,8 @@ /* eslint max-nested-callbacks: ["error", 8] */ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const proxyquire = require('proxyquire').noCallThru() +import { expect } from 'aegir/chai' +import esmock from 'esmock' // @ts-ignore const getBuiltPinata = async (Pinata) => { @@ -15,7 +14,7 @@ const getBuiltPinata = async (Pinata) => { return pinata } -const getPinataNoThrow = () => proxyquire('../../src/pinners/pinata', { +const getPinataNoThrow = () => esmock('../../src/pinners/pinata.js', { 'ipfs-http-client': { globSource: function * () { yield {} @@ -33,12 +32,12 @@ const getPinataNoThrow = () => proxyquire('../../src/pinners/pinata', { }) const getBuiltPinataNoThrow = async () => { - const Pinata = getPinataNoThrow() + const Pinata = await getPinataNoThrow() return getBuiltPinata(Pinata) } const getBuiltPinataThrowAxios = async () => { - const Pinata = proxyquire('../../src/pinners/pinata', { + const Pinata = await esmock('../../src/pinners/pinata.js', { axios: { default: { post: () => { throw new Error() } @@ -49,16 +48,16 @@ const getBuiltPinataThrowAxios = async () => { return getBuiltPinata(Pinata) } -it('pinata constructor throws with missing options', () => { - const Pinata = getPinataNoThrow() +it('pinata constructor throws with missing options', async () => { + const Pinata = await getPinataNoThrow() expect(() => new Pinata({ apiKey: 'somewhere' })).to.throw() }) -it('pinata constructor does not throw with correct options', () => { - const Pinata = getPinataNoThrow() +it('pinata constructor does not throw with correct options', async () => { + const Pinata = await getPinataNoThrow() expect(() => new Pinata({ apiKey: 'apiKey', @@ -83,6 +82,7 @@ it('pinata pinCid throws on HTTP request failure', async () => { }) it('pinata pinDir throws on file system failure', async () => { - const pinata = await getBuiltPinata(require('../../src/pinners/pinata')) + const { default: Pinata } = await import('../../src/pinners/pinata.js') + const pinata = await getBuiltPinata(Pinata) await expect(pinata.pinDir('dir')).to.be.rejected() }) diff --git a/test/utils.spec.js b/test/utils.spec.js index 87e6a33..dc7d6a8 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -1,12 +1,11 @@ /* eslint max-nested-callbacks: ["error", 8] */ /* eslint-env mocha */ -'use strict' -const { expect } = require('aegir/utils/chai') -const proxyquire = require('proxyquire').noCallThru() +import { expect } from 'aegir/chai' +import esmock from 'esmock' -it('guessPath throws when cannot find any guessable path', () => { - const { guessPath } = proxyquire('../src/utils', { +it('guessPath throws when cannot find any guessable path', async () => { + const { guessPath } = await esmock('../src/utils.js', { fs: { existsSync: () => false } @@ -15,8 +14,8 @@ it('guessPath throws when cannot find any guessable path', () => { expect(guessPath).to.throw() }) -it('guessPath throws if more than one guessable path is available', () => { - const { guessPath } = proxyquire('../src/utils', { +it('guessPath throws if more than one guessable path is available', async () => { + const { guessPath } = await esmock('../src/utils.js', { fs: { /** * @param {string} path @@ -38,8 +37,8 @@ it('guessPath throws if more than one guessable path is available', () => { expect(guessPath).to.throw() }) -it('guessPath returns guessable path', () => { - const { guessPath } = proxyquire('../src/utils', { +it('guessPath returns guessable path', async () => { + const { guessPath } = await esmock('../src/utils.js', { fs: { /** * @param {string} path diff --git a/tsconfig.json b/tsconfig.json index 72c6316..abdc60d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { + "module": "node16", + "moduleResolution": "node16", "outDir": "dist", "baseUrl": "./", "paths": {