diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd0e6cb..e1b0cd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: - helia-electron - helia-esbuild - helia-nextjs + - helia-parcel - helia-script-tag - helia-vite - helia-vue @@ -82,6 +83,7 @@ jobs: - helia-create-react-app - helia-electron - helia-esbuild + - helia-parcel - helia-nextjs - helia-script-tag - helia-vite diff --git a/.gitignore b/.gitignore index ab98ddb..ffe3c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ yarn.lock .DS_Store .next .vscode +test-results +playwright-report +.parcel-cache diff --git a/README.md b/README.md index c51429b..fc98bf5 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ - [Table of Contents](#table-of-contents) - [About The Project](#about-the-project) - [Getting Started](#getting-started) + - [Examples](#examples) + - [Basics](#basics) + - [Frameworks](#frameworks) + - [Bundlers](#bundlers) - [Prerequisites](#prerequisites) - [IPFS Tutorials at ProtoSchool](#ipfs-tutorials-at-protoschool) - [Documentation](#documentation) @@ -44,6 +48,28 @@ ## Getting Started +### Examples + +Feel free to jump directly into the examples, however going through the following sections will help build context and background knowledge. + +#### Basics + +- [Helia-101](/examples/helia-101/): Spawn a Helia node, add a file and cat the file +- [Helia CommonJS](/examples/helia-cjs/): Just like Helia-101, but with [CommonJS](https://en.wikipedia.org/wiki/CommonJS) instead of [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) +- [Helia via CDNs](/examples/helia-script-tag/): A simple proof-of-concept to distributing and using helia using ` -
- +
+ IPFS logo
-

Add data to Helia from the browser

+

Add data to Helia

- + Add data to Helia from the browser required /> - + Add data to Helia from the browser

Output

-
+

Listening addresses

+ +
+
+
+

Peers

+ +

Dial Queue

+ +
+
+
diff --git a/examples/helia-esbuild/src/index.js b/examples/helia-esbuild/src/index.js index b54676e..dd9848b 100644 --- a/examples/helia-esbuild/src/index.js +++ b/examples/helia-esbuild/src/index.js @@ -1,17 +1,16 @@ -import { createNode } from './helia.js' import { unixfs } from '@helia/unixfs' +import { createHelia } from 'helia' -const App = () => { - let helia - let fs - +const App = async () => { const DOM = { output: () => document.getElementById('output'), fileName: () => document.getElementById('file-name'), fileContent: () => document.getElementById('file-content'), addBtn: () => document.getElementById('add-submit'), terminal: () => document.getElementById('terminal'), - peers: () => document.getElementById('peers') + peers: () => document.getElementById('peers'), + dialQueue: () => document.getElementById('dialQueue'), + multiaddrs: () => document.getElementById('multiaddrs') } const COLORS = { @@ -55,27 +54,6 @@ const App = () => { } const store = async (name, content) => { - if (!helia) { - showStatus('Creating Helia node', COLORS.active) - - helia = await createNode() - fs = unixfs(helia) - - setInterval(() => { - let peers = '' - - for (const connection of helia.libp2p.getConnections()) { - peers += `${connection.remoteAddr.toString()}\n` - } - - if (peers === '') { - peers = 'Not connected to any peers' - } - - DOM.peers().innerText = peers - }, 500) - } - const id = helia.libp2p.peerId showStatus(`Helia node peer ID ${id}`, COLORS.active) @@ -118,6 +96,54 @@ const App = () => { showStatus(err.message, COLORS.error) } } + + showStatus('Creating Helia node', COLORS.active) + + const helia = await createHelia() + + showStatus('Helia node ready', COLORS.active) + + const fs = unixfs(helia) + + setInterval(() => { + let peers = '' + + for (const connection of helia.libp2p.getConnections()) { + peers += `${connection.remotePeer.toString()}\n` + } + + if (peers === '') { + peers = 'Not connected to any peers' + } + + DOM.peers().innerText = peers + + let dialQueue = '' + + for (const dial of helia.libp2p.getDialQueue()) { + dialQueue += `${dial.peerId} - ${dial.status}\n${dial.multiaddrs.map(ma => ma.toString()).join('\n')}\n` + } + + if (dialQueue === '') { + dialQueue = 'Dial queue empty' + } + + DOM.dialQueue().innerText = dialQueue + + let multiaddrs = '' + + for (const ma of helia.libp2p.getMultiaddrs()) { + multiaddrs += `${ma.toString()}\n` + } + + if (multiaddrs === '') { + multiaddrs = 'Not listening on any addresses' + } + + DOM.multiaddrs().innerText = multiaddrs + }, 500) } -App() +App().catch(err => { + console.error(err) // eslint-disable-line no-console +}) diff --git a/examples/helia-esbuild/src/ipfs-logo.svg b/examples/helia-esbuild/src/ipfs-logo.svg deleted file mode 100644 index f8633f6..0000000 --- a/examples/helia-esbuild/src/ipfs-logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/helia-esbuild/src/style.css b/examples/helia-esbuild/src/style.css index 1da94e5..e118283 100644 --- a/examples/helia-esbuild/src/style.css +++ b/examples/helia-esbuild/src/style.css @@ -48,3 +48,8 @@ form { .terminal a:hover { color: #fff } + +.terminal p { + margin: 0; + margin-bottom: 0.5em; +} diff --git a/examples/helia-esbuild/tests/test.js b/examples/helia-esbuild/test/index.spec.js similarity index 66% rename from examples/helia-esbuild/tests/test.js rename to examples/helia-esbuild/test/index.spec.js index f0be64f..804c31f 100644 --- a/examples/helia-esbuild/tests/test.js +++ b/examples/helia-esbuild/test/index.spec.js @@ -1,23 +1,23 @@ -import { test, expect } from '@playwright/test' -import { playwright } from 'test-util-ipfs-example' +import { setup, expect } from 'test-ipfs-example/browser' // Setup -const play = test.extend({ - ...playwright.servers() -}) +const test = setup() -play.describe('bundle ipfs with esbuild:', () => { +test.describe('bundle ipfs with esbuild:', () => { // DOM const nameInput = '#file-name' const contentInput = '#file-content' const submitBtn = '#add-submit' const output = '#output' - play.beforeEach(async ({ servers, page }) => { - await page.goto(`http://localhost:${servers[0].port}/`) + test.beforeEach(async ({ servers, page }) => { + await page.goto(servers[0].url) }) - play('should initialize a Helia node and add/get a file', async ({ page }) => { + test('should initialize a Helia node and add/get a file', async ({ page }) => { + const outputLocator = page.locator(output) + await expect(outputLocator).toHaveText(/Helia node ready/) + const fileName = 'test.txt' const fileContent = 'Hello world!' @@ -28,7 +28,6 @@ play.describe('bundle ipfs with esbuild:', () => { await page.waitForSelector(`${output}:has-text("/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi")`) const outputContent = await page.textContent(output) - expect(outputContent).toContain('https://ipfs.io/ipfs/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi') expect(outputContent).toContain(fileName) expect(outputContent).toContain(fileContent) diff --git a/examples/helia-nextjs/components/ipfs.js b/examples/helia-nextjs/components/ipfs.js index 4ff5df1..3fed0a9 100644 --- a/examples/helia-nextjs/components/ipfs.js +++ b/examples/helia-nextjs/components/ipfs.js @@ -1,7 +1,6 @@ +import { createHelia } from 'helia' import { React, useState, useEffect } from 'react' -import getHelia from '../lib/get-helia.js' - const IpfsComponent = () => { const [id, setId] = useState(null) const [helia, setHelia] = useState(null) @@ -11,7 +10,7 @@ const IpfsComponent = () => { const init = async () => { if (helia) return - const heliaNode = await getHelia() + const heliaNode = await createHelia() const nodeId = heliaNode.libp2p.peerId.toString() const nodeIsOnline = heliaNode.libp2p.isStarted() diff --git a/examples/helia-nextjs/lib/get-helia.js b/examples/helia-nextjs/lib/get-helia.js deleted file mode 100644 index e89158f..0000000 --- a/examples/helia-nextjs/lib/get-helia.js +++ /dev/null @@ -1,18 +0,0 @@ -import { createHelia } from 'helia' -import { MemoryBlockstore } from 'blockstore-core' -import { MemoryDatastore } from 'datastore-core' -import getLibp2p from './get-libp2p.js' - -export default async function getHelia () { - const blockstore = new MemoryBlockstore() - const datastore = new MemoryDatastore() - - const libp2p = await getLibp2p({ datastore }) - - // create a Helia node - return await createHelia({ - datastore, - blockstore, - libp2p - }) -} diff --git a/examples/helia-nextjs/lib/get-libp2p.js b/examples/helia-nextjs/lib/get-libp2p.js deleted file mode 100644 index 7a876da..0000000 --- a/examples/helia-nextjs/lib/get-libp2p.js +++ /dev/null @@ -1,30 +0,0 @@ -import { createLibp2p } from 'libp2p' -import { noise } from '@chainsafe/libp2p-noise' -import { yamux } from '@chainsafe/libp2p-yamux' -import { webSockets } from '@libp2p/websockets' -import { bootstrap } from '@libp2p/bootstrap' - -export default async function getLibp2p ({ datastore }) { - return await createLibp2p({ - datastore, - transports: [ - webSockets() - ], - connectionEncryption: [ - noise() - ], - streamMuxers: [ - yamux() - ], - peerDiscovery: [ - bootstrap({ - list: [ - '/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt' - ] - }) - ] - }) -} diff --git a/examples/helia-nextjs/next.config.js b/examples/helia-nextjs/next.config.js index ef26a4f..10886e0 100644 --- a/examples/helia-nextjs/next.config.js +++ b/examples/helia-nextjs/next.config.js @@ -1,9 +1,7 @@ export default { reactStrictMode: true, - // https://github.com/vercel/next.js/issues/21079 - // Remove the workaround the issue is fixed images: { loader: 'imgix', - path: 'https://noop/' + path: 'http://localhost:3000' } } diff --git a/examples/helia-nextjs/package.json b/examples/helia-nextjs/package.json index 1fced3a..afc6c30 100644 --- a/examples/helia-nextjs/package.json +++ b/examples/helia-nextjs/package.json @@ -11,17 +11,10 @@ "serve": "npm run dev", "start": "next start", "lint": "next lint", - "test": "npm run build && playwright test tests" + "test": "npm run build && test-browser-example test" }, "dependencies": { - "@chainsafe/libp2p-noise": "^11.0.2", - "@chainsafe/libp2p-yamux": "^3.0.7", - "@libp2p/bootstrap": "^6.0.3", - "@libp2p/websockets": "^5.0.7", - "blockstore-core": "^4.1.0", - "datastore-core": "^9.1.0", - "helia": "^1.0.0", - "libp2p": "^0.43.3", + "helia": "^1.3.8", "next": "^13.2.4", "react": "^18.2.0", "react-dom": "^18.2.0" @@ -32,7 +25,7 @@ "eslint-config-next": "^13.2.4", "interface-datastore": "^8.2.0", "playwright": "^1.32.1", - "rimraf": "^4.4.1", - "test-util-ipfs-example": "^1.0.2" + "rimraf": "^5.0.0", + "test-ipfs-example": "^1.0.0" } } diff --git a/examples/helia-nextjs/pages/_app.js b/examples/helia-nextjs/pages/_app.js index 46f5380..952677f 100644 --- a/examples/helia-nextjs/pages/_app.js +++ b/examples/helia-nextjs/pages/_app.js @@ -1,5 +1,5 @@ -import { React } from 'react' import PropTypes from 'prop-types' +import { React } from 'react' import '../styles/globals.css' function MyApp ({ Component, pageProps }) { @@ -7,7 +7,7 @@ function MyApp ({ Component, pageProps }) { } MyApp.propTypes = { - Component: PropTypes.object, + Component: PropTypes.func, pageProps: PropTypes.object } diff --git a/examples/helia-nextjs/pages/index.js b/examples/helia-nextjs/pages/index.js index 5a6ba24..6154bdf 100644 --- a/examples/helia-nextjs/pages/index.js +++ b/examples/helia-nextjs/pages/index.js @@ -1,9 +1,8 @@ -import { React } from 'react' import Head from 'next/head' -import Image from 'next/image' -import styles from '../styles/Home.module.css' - +import Image from 'next/legacy/image' +import { React } from 'react' import IpfsComponent from '../components/ipfs' +import styles from '../styles/Home.module.css' export default function Home () { return ( @@ -11,7 +10,7 @@ export default function Home () { Create Next App - +
diff --git a/examples/helia-nextjs/public/favicon.ico b/examples/helia-nextjs/public/favicon.ico deleted file mode 100644 index b2f1f96..0000000 Binary files a/examples/helia-nextjs/public/favicon.ico and /dev/null differ diff --git a/examples/helia-nextjs/test/index.spec.js b/examples/helia-nextjs/test/index.spec.js new file mode 100644 index 0000000..222880c --- /dev/null +++ b/examples/helia-nextjs/test/index.spec.js @@ -0,0 +1,21 @@ +import { setup, expect } from 'test-ipfs-example/browser' + +// Setup +const test = setup() + +test.describe('integrate ipfs with nextjs:', () => { + // DOM + const id = '[data-test=id]' + const status = '[data-test=status]' + + test.beforeEach(async ({ servers, page }) => { + await page.goto(servers[0].url) + }) + + test('should properly initialized a IPFS node and print some properties', async ({ page }) => { + await page.waitForSelector(id) + + expect(await page.isVisible(id)).toBeTruthy() + expect(await page.textContent(status)).toContain('Online') + }) +}) diff --git a/examples/helia-nextjs/tests/test.js b/examples/helia-nextjs/tests/test.js deleted file mode 100644 index 46b5948..0000000 --- a/examples/helia-nextjs/tests/test.js +++ /dev/null @@ -1,24 +0,0 @@ -import { test, expect } from '@playwright/test' -import { playwright } from 'test-util-ipfs-example' - -// Setup -const play = test.extend({ - ...playwright.servers() -}) - -play.describe('integrate ipfs with nextjs:', () => { - // DOM - const id = '[data-test=id]' - const status = '[data-test=status]' - - play.beforeEach(async ({ servers, page }) => { - await page.goto(`http://localhost:${servers[0].port}/`) - }) - - play('should properly initialized a IPFS node and print some properties', async ({ page }) => { - await page.waitForSelector(id) - - expect(await page.isVisible(id)).toBeTruthy() - expect(await page.textContent(status)).toContain('Online') - }) -}) diff --git a/examples/helia-parcel/.github/pull_request_template.md b/examples/helia-parcel/.github/pull_request_template.md new file mode 100644 index 0000000..04bd7d5 --- /dev/null +++ b/examples/helia-parcel/.github/pull_request_template.md @@ -0,0 +1,17 @@ +# ⚠️ IMPORTANT ⚠️ + +# Please do not create a Pull Request for this repository + +The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. + +Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. + +## Contributing + +Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. + +1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) +2. Create your Feature Branch (`git checkout -b feature/amazing-example`) +3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) +4. Push to the Branch (`git push origin feature/amazing-example`) +5. Open a Pull Request diff --git a/examples/helia-parcel/.github/workflows/sync.yml b/examples/helia-parcel/.github/workflows/sync.yml new file mode 100644 index 0000000..3f8b744 --- /dev/null +++ b/examples/helia-parcel/.github/workflows/sync.yml @@ -0,0 +1,19 @@ +name: pull + +on: + workflow_dispatch + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Pull from another repository + uses: ipfs-examples/actions-pull-directory-from-repo@main + with: + source-repo: ipfs-examples/helia-examples + source-folder-path: examples/${{ github.event.repository.name }} + source-branch: main + target-branch: main + git-username: github-actions + git-email: github-actions@github.com diff --git a/examples/helia-parcel/README.md b/examples/helia-parcel/README.md new file mode 100644 index 0000000..3164d6d --- /dev/null +++ b/examples/helia-parcel/README.md @@ -0,0 +1,105 @@ +

+ + Helia logo + +

+ +

Helia with parcel

+ +

+ +
+ Explore the docs + · + View Demo + · + Report Bug + · + Request Feature/Example +

+ +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [About The Project](#about-the-project) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Installation and Running example](#installation-and-running-example) +- [Usage](#usage) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [Want to hack on IPFS?](#want-to-hack-on-ipfs) + +## About The Project + +- Read the [docs](https://ipfs.github.io/helia/modules/helia.html) +- Look into other [examples](https://github.com/ipfs-examples/helia-examples) to learn how to spawn a Helia node in Node.js and in the Browser +- Visit https://dweb-primer.ipfs.io to learn about IPFS and the concepts that underpin it +- Head over to https://proto.school to take interactive tutorials that cover core IPFS APIs +- Check out https://docs.ipfs.io for tips, how-tos and more +- See https://blog.ipfs.io for news and more +- Need help? Please ask 'How do I?' questions on https://discuss.ipfs.io + +## Getting Started + +### Prerequisites + +Make sure you have installed all of the following prerequisites on your development machine: + +- Git - [Download & Install Git](https://git-scm.com/downloads). OSX and Linux machines typically have this already installed. +- Node.js - [Download & Install Node.js](https://nodejs.org/en/download/) and the npm package manager. + +### Installation and Running example + +```console +> npm install +> npm start +``` + +Now open your browser at `http://localhost:8888` + +## Usage + +In this example, you will find a boilerplate you can use to guide yourself into bundling js-ipfs with [parcel](https://parceljs.org/), so that you can use it in your own web app! + +You should see the following: + +![](./img/1.png) +![](./img/2.png) + +_For more examples, please refer to the [Documentation](#documentation)_ + +## Documentation + +- [IPFS Primer](https://dweb-primer.ipfs.io/) +- [IPFS Docs](https://docs.ipfs.io/) +- [Tutorials](https://proto.school) +- [More examples](https://github.com/ipfs-examples/helia-examples) +- [API - Helia](https://ipfs.github.io/helia/modules/helia.html) +- [API - @helia/unixfs](https://ipfs.github.io/helia-unixfs/modules/helia.html) + +## Contributing + +Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. + +1. Fork the IPFS Project +2. Create your Feature Branch (`git checkout -b feature/amazing-feature`) +3. Commit your Changes (`git commit -a -m 'feat: add some amazing feature'`) +4. Push to the Branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## Want to hack on IPFS? + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) + +The IPFS implementation in JavaScript needs your help! There are a few things you can do right now to help out: + +Read the [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md) and [JavaScript Contributing Guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md). + +- **Check out existing issues** The [issue list](https://github.com/ipfs/helia/issues) has many that are marked as ['help wanted'](https://github.com/ipfs/helia/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22help+wanted%22) or ['difficulty:easy'](https://github.com/ipfs/helia/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Adifficulty%3Aeasy) which make great starting points for development, many of which can be tackled with no prior IPFS knowledge +- **Look at the [Helia Roadmap](https://github.com/ipfs/helia/blob/main/ROADMAP.md)** This are the high priority items being worked on right now +- **Perform code reviews** More eyes will help + a. speed the project along + b. ensure quality, and + c. reduce possible future bugs +- **Add tests**. There can never be enough tests diff --git a/examples/helia-parcel/img/1.png b/examples/helia-parcel/img/1.png new file mode 100644 index 0000000..1bf37bd Binary files /dev/null and b/examples/helia-parcel/img/1.png differ diff --git a/examples/helia-parcel/img/2.png b/examples/helia-parcel/img/2.png new file mode 100644 index 0000000..83afeb8 Binary files /dev/null and b/examples/helia-parcel/img/2.png differ diff --git a/examples/helia-parcel/package.json b/examples/helia-parcel/package.json new file mode 100644 index 0000000..4487a09 --- /dev/null +++ b/examples/helia-parcel/package.json @@ -0,0 +1,34 @@ +{ + "name": "helia-parcel", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "Bundle Helia with parcel", + "keywords": [], + "license": "ISC", + "author": "", + "scripts": { + "clean": "rimraf ./dist ./.cache ./.parcel-cache", + "build": "parcel build src/index.html", + "serve": "parcel serve src/index.html --open -p 8888", + "start": "npm run serve", + "test": "npm run build && playwright test test" + }, + "browserslist": "last 1 Chrome version", + "dependencies": { + "@helia/unixfs": "1.2.1", + "helia": "1.3.8" + }, + "devDependencies": { + "@babel/core": "^7.14.8", + "@playwright/test": "^1.12.3", + "parcel": "^2.9.1", + "playwright": "^1.12.3", + "process": "^0.11.10", + "rimraf": "^5.0.0", + "test-ipfs-example": "^1.0.0" + }, + "@parcel/resolver-default": { + "packageExports": true + } +} diff --git a/examples/helia-parcel/src/index.html b/examples/helia-parcel/src/index.html new file mode 100644 index 0000000..3028497 --- /dev/null +++ b/examples/helia-parcel/src/index.html @@ -0,0 +1,106 @@ + + + + + + + Bundle Helia with parcel + + + + + + + + + +
+ + Helia logo + +
+ +
+

Add data to Helia

+ + + + + + + + + + + +

Output

+ +
+
+
+
+
+ +

Listening addresses

+ +
+
+
+ +

Peers

+ +
+
+
+ +

Dial Queue

+ +
+
+
+
+ + diff --git a/examples/helia-parcel/src/index.js b/examples/helia-parcel/src/index.js new file mode 100644 index 0000000..dd9848b --- /dev/null +++ b/examples/helia-parcel/src/index.js @@ -0,0 +1,149 @@ +import { unixfs } from '@helia/unixfs' +import { createHelia } from 'helia' + +const App = async () => { + const DOM = { + output: () => document.getElementById('output'), + fileName: () => document.getElementById('file-name'), + fileContent: () => document.getElementById('file-content'), + addBtn: () => document.getElementById('add-submit'), + terminal: () => document.getElementById('terminal'), + peers: () => document.getElementById('peers'), + dialQueue: () => document.getElementById('dialQueue'), + multiaddrs: () => document.getElementById('multiaddrs') + } + + const COLORS = { + active: '#357edd', + success: '#0cb892', + error: '#ea5037' + } + + const scrollToBottom = () => { + const terminal = DOM.terminal() + terminal.scroll({ top: terminal.scrollHeight, behavior: 'smooth' }) + } + + const showStatus = (text, bg, id = null) => { + const log = DOM.output() + + const line = document.createElement('p') + line.innerHTML = text + line.style.color = bg + + if (id) { + line.id = id + } + + log.appendChild(line) + + scrollToBottom(log) + } + + const cat = async (cid) => { + const decoder = new TextDecoder() + let content = '' + + for await (const chunk of fs.cat(cid)) { + content += decoder.decode(chunk, { + stream: true + }) + } + + return content + } + + const store = async (name, content) => { + const id = helia.libp2p.peerId + showStatus(`Helia node peer ID ${id}`, COLORS.active) + + const fileToAdd = { + path: `${name}`, + content: new TextEncoder().encode(content) + } + + showStatus(`Adding file ${fileToAdd.path}`, COLORS.active) + const cid = await fs.addFile(fileToAdd) + + showStatus(`Added ${cid}`, COLORS.success, cid) + showStatus('Reading file', COLORS.active) + + const text = await cat(cid) + + showStatus(`\u2514\u2500 ${name} ${text.toString()}`) + showStatus(`Preview: https://ipfs.io/ipfs/${cid}`, COLORS.success) + } + + // Event listeners + DOM.addBtn().onclick = async (e) => { + e.preventDefault() + let name = DOM.fileName().value + let content = DOM.fileContent().value + + try { + if (name == null || name.trim() === '') { + showStatus('Set default name', COLORS.active) + name = 'test.txt' + } + + if ((content == null || content.trim() === '')) { + showStatus('Set default content', COLORS.active) + content = 'Hello world!' + } + + await store(name, content) + } catch (err) { + showStatus(err.message, COLORS.error) + } + } + + showStatus('Creating Helia node', COLORS.active) + + const helia = await createHelia() + + showStatus('Helia node ready', COLORS.active) + + const fs = unixfs(helia) + + setInterval(() => { + let peers = '' + + for (const connection of helia.libp2p.getConnections()) { + peers += `${connection.remotePeer.toString()}\n` + } + + if (peers === '') { + peers = 'Not connected to any peers' + } + + DOM.peers().innerText = peers + + let dialQueue = '' + + for (const dial of helia.libp2p.getDialQueue()) { + dialQueue += `${dial.peerId} - ${dial.status}\n${dial.multiaddrs.map(ma => ma.toString()).join('\n')}\n` + } + + if (dialQueue === '') { + dialQueue = 'Dial queue empty' + } + + DOM.dialQueue().innerText = dialQueue + + let multiaddrs = '' + + for (const ma of helia.libp2p.getMultiaddrs()) { + multiaddrs += `${ma.toString()}\n` + } + + if (multiaddrs === '') { + multiaddrs = 'Not listening on any addresses' + } + + DOM.multiaddrs().innerText = multiaddrs + }, 500) +} + +App().catch(err => { + console.error(err) // eslint-disable-line no-console +}) diff --git a/examples/helia-parcel/src/style.css b/examples/helia-parcel/src/style.css new file mode 100644 index 0000000..e118283 --- /dev/null +++ b/examples/helia-parcel/src/style.css @@ -0,0 +1,55 @@ +::placeholder { + color: rgb(0 0 0 / 30%); +} + +form { + margin: 1.25rem 0; +} + +.window { + display: flex; + flex-direction: column; + background: #222; + color: #fff; + height: 400px; +} + +.window .header { + flex-basis: 3rem; + background: #c6c6c6; + position: relative; +} + +.window .header:after { + content: ". . ."; + position: absolute; + left: 12px; + right: 0; + top: -3px; + font-family: "Times New Roman", Times, serif; + font-size: 96px; + color: #fff; + line-height: 0; + letter-spacing: -12px; +} + +.terminal { + margin: 20px; + font-family: monospace; + font-size: 16px; + overflow: auto; + flex: 1; +} + +.terminal a, .terminal a:active, .terminal a:visited { + color: #0cb892 +} + +.terminal a:hover { + color: #fff +} + +.terminal p { + margin: 0; + margin-bottom: 0.5em; +} diff --git a/examples/helia-parcel/test/index.spec.js b/examples/helia-parcel/test/index.spec.js new file mode 100644 index 0000000..22be908 --- /dev/null +++ b/examples/helia-parcel/test/index.spec.js @@ -0,0 +1,41 @@ +import { setup, expect } from 'test-ipfs-example/browser' + +// Setup +const test = setup() + +test.describe('bundle ipfs with parcel:', () => { + // DOM + const nameInput = '#file-name' + const contentInput = '#file-content' + const submitBtn = '#add-submit' + const output = '#output' + + test.beforeEach(async ({ servers, page }) => { + await page.goto(servers[0].url) + }) + + /** + * This example is breaks due to `Uncaught Error: Cannot find module 'fs'` when updating helia deps. + * It fails due to `Please configure Helia with a libp2p instance` without upgrading the deps. + * + * @see https://github.com/ipfs-examples/helia-examples/issues/87 + */ + test.skip('should initialize a Helia node and add/get a file', async ({ page }) => { + const outputLocator = page.locator(output) + await expect(outputLocator).toHaveText(/Creating Helia node/) + + const fileName = 'test.txt' + const fileContent = 'Hello world!' + + await page.fill(nameInput, fileName) + await page.fill(contentInput, fileContent) + await page.click(submitBtn) + + await page.waitForSelector(`${output}:has-text("/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi")`) + + const outputContent = await page.textContent(output) + expect(outputContent).toContain('https://ipfs.io/ipfs/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi') + expect(outputContent).toContain(fileName) + expect(outputContent).toContain(fileContent) + }) +}) diff --git a/examples/helia-script-tag/README.md b/examples/helia-script-tag/README.md index 5f211de..9aa6e6f 100644 --- a/examples/helia-script-tag/README.md +++ b/examples/helia-script-tag/README.md @@ -11,7 +11,7 @@
Explore the docs . - View codesandbox Demo + View codesandbox Demo · Report Bug · diff --git a/examples/helia-script-tag/favicon.ico b/examples/helia-script-tag/favicon.ico deleted file mode 100644 index b2f1f96..0000000 Binary files a/examples/helia-script-tag/favicon.ico and /dev/null differ diff --git a/examples/helia-script-tag/index.html b/examples/helia-script-tag/index.html index b676db3..b879b6f 100644 --- a/examples/helia-script-tag/index.html +++ b/examples/helia-script-tag/index.html @@ -6,7 +6,7 @@ IPFS in the Browser via Helia - + @@ -106,6 +106,7 @@

Event Log:

+ diff --git a/examples/helia-script-tag/package.json b/examples/helia-script-tag/package.json index 4f01123..8b271ce 100644 --- a/examples/helia-script-tag/package.json +++ b/examples/helia-script-tag/package.json @@ -11,7 +11,7 @@ "build": "vite build", "serve": "vite dev --port 8888", "start": "npm run serve", - "test": "npm run build && playwright test tests" + "test": "npm run build && test-browser-example test" }, "browserslist": "last 1 Chrome version", "devDependencies": { @@ -19,12 +19,8 @@ "@playwright/test": "^1.12.3", "playwright": "^1.12.3", "process": "^0.11.10", - "rimraf": "^4.4.0", - "test-util-ipfs-example": "^1.0.2", - "util": "^0.12.4", + "rimraf": "^5.0.0", + "test-ipfs-example": "^1.0.0", "vite": "^4.2.1" - }, - "dependencies": { - "@chainsafe/libp2p-noise": "^11.0.1" } } diff --git a/examples/helia-script-tag/src/createLibp2pInstance.js b/examples/helia-script-tag/src/createLibp2pInstance.js deleted file mode 100644 index f73a000..0000000 --- a/examples/helia-script-tag/src/createLibp2pInstance.js +++ /dev/null @@ -1,88 +0,0 @@ -/* global Libp2P, ChainsafeLibp2PYamux, Libp2PWebsockets, Libp2PBootstrap, Libp2PKadDht */ -// not imported from skypack,jsdelivr, nor unpkg because of issues. see https://github.com/ChainSafe/js-libp2p-noise/issues/296 -import { noise } from 'https://esm.sh/v111/@chainsafe/libp2p-noise@11.0.1/es2022/libp2p-noise.js' - -/** - * - * @param {object} param0 - * @param {import('libp2p').Libp2pOptions['datastore']} param0.datastore - */ -export async function createLibp2pInstance ({ datastore }) { - /** @type {Partial} */ - const libp2pInit = { - /** - * @see https://github.com/ipfs/js-ipfs-interfaces/tree/master/packages/interface-datastore - */ - datastore, - /** - * @see https://github.com/libp2p/js-libp2p-kad-dht - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-dht - */ - dht: /** @type {import('libp2p').Libp2pOptions['dht']} */(Libp2PKadDht.kadDHT({ // The DHT options (and defaults) can be found in its documentation - kBucketSize: 5, - clientMode: true, - enabled: true, - randomWalk: { - enabled: true, // Allows to disable discovery (enabled by default) - interval: 300e3, - timeout: 10e3 - } - })), - /** - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#transport - */ - transports: /** @type {import('libp2p').Libp2pOptions['transports']} */([ - Libp2PWebsockets.webSockets() - ]), - /** - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#connection-encryption - */ - connectionEncryption: /** @type {import('libp2p').Libp2pOptions['connectionEncryption']} */([ - noise() - ]), - /** - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#stream-multiplexing - */ - streamMuxers: /** @type {import('libp2p').Libp2pOptions['streamMuxers']} */([ - ChainsafeLibp2PYamux.yamux() - ]), - /** - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-peer-discovery - */ - peerDiscovery: /** @type {import('libp2p').Libp2pOptions['peerDiscovery']} */([ - Libp2PBootstrap.bootstrap({ - list: [ - '/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt' - ] - }) - ]), - /** - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#configuring-connection-manager - */ - connectionManager: /** @type {import('libp2p').Libp2pOptions['connectionManager']} */({ - // Auto connect to discovered peers (limited by ConnectionManager minConnections) - autoDial: true - }), - /** - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#configuring-peerstore - */ - peerStore: /** @type {import('libp2p').Libp2pOptions['peerStore']} */({ - persistence: true, - /** - * The threshold number represents the maximum number of "dirty peers" allowed in the PeerStore, i.e. peers that - * are not updated in the datastore. In this context, browser nodes should use a threshold of 1, since they - * might not "stop" properly in several scenarios and the PeerStore might end up with unflushed records when the - * window is closed. - */ - threshold: 1 - }) - } - - /** @type {import('libp2p').Libp2p} */ - const libp2pInstance = await Libp2P.createLibp2p(libp2pInit) - - return libp2pInstance -} diff --git a/examples/helia-script-tag/src/index.js b/examples/helia-script-tag/src/index.js index 9772c42..0ea6f69 100644 --- a/examples/helia-script-tag/src/index.js +++ b/examples/helia-script-tag/src/index.js @@ -1,7 +1,5 @@ /* global Helia, BlockstoreCore, DatastoreCore, HeliaUnixfs */ -import { createLibp2pInstance } from './createLibp2pInstance.js' - const statusValueEl = document.getElementById('statusValue') const discoveredPeerCountEl = document.getElementById('discoveredPeerCount') const connectedPeerCountEl = document.getElementById('connectedPeerCount') @@ -19,14 +17,14 @@ document.addEventListener('DOMContentLoaded', async () => { }) helia.libp2p.addEventListener('peer:connect', (evt) => { - addToLog(`Connected to ${evt.detail.remotePeer.toString()}`) + addToLog(`Connected to ${evt.detail.toString()}`) }) helia.libp2p.addEventListener('peer:disconnect', (evt) => { - addToLog(`Disconnected from ${evt.detail.remotePeer.toString()}`) + addToLog(`Disconnected from ${evt.detail.toString()}`) }) setInterval(() => { - statusValueEl.innerHTML = helia.libp2p.started ? 'Online' : 'Offline' + statusValueEl.innerHTML = helia.libp2p.isStarted() ? 'Online' : 'Offline' updateConnectedPeers() updateDiscoveredPeers() }, 500) @@ -73,7 +71,6 @@ const addToLog = (msg) => { } let heliaInstance = null -let libp2pInstance = null const instantiateHeliaNode = async () => { // application-specific data lives in the datastore const datastore = new DatastoreCore.MemoryDatastore() @@ -83,18 +80,9 @@ const instantiateHeliaNode = async () => { return heliaInstance } - if (libp2pInstance == null) { - /** - * @see https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-libp2p - */ - libp2pInstance = await createLibp2pInstance({ datastore }) - addToLog('Created LibP2P instance') - } - heliaInstance = await Helia.createHelia({ datastore, - blockstore, - libp2p: libp2pInstance + blockstore }) addToLog('Created Helia instance') diff --git a/examples/helia-script-tag/tests/test.js b/examples/helia-script-tag/test/index.spec.js similarity index 59% rename from examples/helia-script-tag/tests/test.js rename to examples/helia-script-tag/test/index.spec.js index 3823acc..653d5bb 100644 --- a/examples/helia-script-tag/tests/test.js +++ b/examples/helia-script-tag/test/index.spec.js @@ -1,22 +1,19 @@ -import { test } from '@playwright/test' -import { playwright } from 'test-util-ipfs-example' +import { setup } from 'test-ipfs-example/browser' // Setup -const play = test.extend({ - ...playwright.servers() -}) +const test = setup() -play.describe('using script tag:', () => { +test.describe('using script tag:', () => { // DOM const status = '#statusValue' const node = '#nodeId' const stopHelia = '.e2e-stopHelia' - play.beforeEach(async ({ servers, page }) => { - await page.goto(`http://localhost:${servers[0].port}/`) + test.beforeEach(async ({ servers, page }) => { + await page.goto(servers[0].url) }) - play('should properly initialized a IPFS node and print the status', async ({ page }) => { + test('should properly initialized a IPFS node and print the status', async ({ page }) => { // wait for page to init await page.waitForSelector(`${status}:has-text("Not Started")`) diff --git a/examples/helia-vite/package.json b/examples/helia-vite/package.json index b3cf444..e7c4456 100644 --- a/examples/helia-vite/package.json +++ b/examples/helia-vite/package.json @@ -10,29 +10,22 @@ "start": "vite", "build": "vite build", "preview": "vite preview", - "test": "npm run build && playwright test tests" + "test": "npm run build && test-browser-example test" }, "dependencies": { - "@chainsafe/libp2p-noise": "^11.0.1", - "@chainsafe/libp2p-yamux": "^3.0.7", - "@helia/unixfs": "^1.2.0", - "@libp2p/bootstrap": "^6.0.2", - "@libp2p/websockets": "^5.0.6", - "blockstore-core": "^4.0.1", - "datastore-core": "^9.0.3", + "@helia/unixfs": "^1.4.1", "helia": "^1.0.0", - "libp2p": "^0.43.3", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "rimraf": "^4.4.0" + "rimraf": "^5.0.0" }, "devDependencies": { "@playwright/test": "^1.31.2", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^3.1.0", - "test-util-ipfs-example": "^1.0.2", + "@vitejs/plugin-react": "^4.0.0", + "test-ipfs-example": "^1.0.0", "vite": "^4.2.0" } } diff --git a/examples/helia-vite/src/App.jsx b/examples/helia-vite/src/App.jsx index f5509f7..82c23f7 100644 --- a/examples/helia-vite/src/App.jsx +++ b/examples/helia-vite/src/App.jsx @@ -1,7 +1,7 @@ import { React, useState } from 'react' import './App.css' -import { useHelia } from '@/hooks/useHelia' import { useCommitText } from '@/hooks/useCommitText' +import { useHelia } from '@/hooks/useHelia' function App () { const [text, setText] = useState('') diff --git a/examples/helia-vite/src/hooks/useCommitText.jsx b/examples/helia-vite/src/hooks/useCommitText.jsx index 035ce92..bfbd007 100644 --- a/examples/helia-vite/src/hooks/useCommitText.jsx +++ b/examples/helia-vite/src/hooks/useCommitText.jsx @@ -1,3 +1,5 @@ +/* eslint-disable no-console */ + import { useState, useCallback } from 'react' import { useHelia } from '@/hooks/useHelia' diff --git a/examples/helia-vite/src/provider/HeliaProvider.jsx b/examples/helia-vite/src/provider/HeliaProvider.jsx index 4fa4b8c..4c88386 100644 --- a/examples/helia-vite/src/provider/HeliaProvider.jsx +++ b/examples/helia-vite/src/provider/HeliaProvider.jsx @@ -1,3 +1,8 @@ +/* eslint-disable no-console */ + +import { unixfs } from '@helia/unixfs' +import { createHelia } from 'helia' +import PropTypes from 'prop-types' import { React, useEffect, @@ -5,16 +10,6 @@ import { useCallback, createContext } from 'react' -import { createHelia } from 'helia' -import { createLibp2p } from 'libp2p' -import { noise } from '@chainsafe/libp2p-noise' -import { yamux } from '@chainsafe/libp2p-yamux' -import { webSockets } from '@libp2p/websockets' -import { bootstrap } from '@libp2p/bootstrap' -import { unixfs } from '@helia/unixfs' -import { MemoryBlockstore } from 'blockstore-core' -import { MemoryDatastore } from 'datastore-core' -import PropTypes from 'prop-types' export const HeliaContext = createContext({ helia: null, @@ -39,42 +34,8 @@ export const HeliaProvider = ({ children }) => { setStarting(false) } else { try { - // the blockstore is where we store the blocks that make up files - const blockstore = new MemoryBlockstore() - - // application-specific data lives in the datastore - const datastore = new MemoryDatastore() - - // libp2p is the networking layer that underpins Helia - // Make sure to stick libp2p here when running react in strict mode - const libp2p = await createLibp2p({ - datastore, - transports: [ - webSockets() - ], - connectionEncryption: [ - noise() - ], - streamMuxers: [ - yamux() - ], - peerDiscovery: [ - bootstrap({ - list: [ - '/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - '/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt' - ] - }) - ] - }) console.info('Starting Helia') - const helia = await createHelia({ - datastore, - blockstore, - libp2p - }) + const helia = await createHelia() setHelia(helia) setFs(unixfs(helia)) setStarting(false) diff --git a/examples/helia-vite/tests/test.js b/examples/helia-vite/test/index.spec.js similarity index 77% rename from examples/helia-vite/tests/test.js rename to examples/helia-vite/test/index.spec.js index 2bd94a0..a5d018e 100644 --- a/examples/helia-vite/tests/test.js +++ b/examples/helia-vite/test/index.spec.js @@ -1,11 +1,9 @@ -import { test, expect } from '@playwright/test' -import { playwright } from 'test-util-ipfs-example' +import { setup, expect } from 'test-ipfs-example/browser' -const play = test.extend({ - ...playwright.servers() -}) +// Setup +const test = setup() -play.describe('Use Helia With react and vite', () => { +test.describe('Use Helia With react and vite', () => { // DOM const heliaStatus = '#heliaStatus' const textInput = '#textInput' @@ -14,11 +12,11 @@ play.describe('Use Helia With react and vite', () => { const fetchCommittedTextButton = '#fetchCommittedTextButton' const committedTextOutput = '#committedTextOutput' - play.beforeEach(async ({ servers, page }) => { - await page.goto(`http://localhost:${servers[0].port}/`) + test.beforeEach(async ({ servers, page }) => { + await page.goto(servers[0].url) }) - play('should properly initialize a Helia node and add/get a file', async ({ page }) => { + test('should properly initialize a Helia node and add/get a file', async ({ page }) => { // wait for helia node to be online const text = 'Hello Helia' const status = await page.locator(heliaStatus) diff --git a/examples/helia-vite/vite.config.js b/examples/helia-vite/vite.config.js index eeb9eee..b4a5744 100644 --- a/examples/helia-vite/vite.config.js +++ b/examples/helia-vite/vite.config.js @@ -1,6 +1,6 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' import path from 'path' +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], diff --git a/examples/helia-vue/.eslintrc.cjs b/examples/helia-vue/.eslintrc.cjs deleted file mode 100644 index b64731a..0000000 --- a/examples/helia-vue/.eslintrc.cjs +++ /dev/null @@ -1,14 +0,0 @@ -/* eslint-env node */ -require('@rushstack/eslint-patch/modern-module-resolution') - -module.exports = { - root: true, - 'extends': [ - 'plugin:vue/vue3-essential', - 'eslint:recommended', - '@vue/eslint-config-prettier/skip-formatting' - ], - parserOptions: { - ecmaVersion: 'latest' - } -} diff --git a/examples/helia-vue/.prettierrc.json b/examples/helia-vue/.prettierrc.json deleted file mode 100644 index 66e2335..0000000 --- a/examples/helia-vue/.prettierrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/prettierrc", - "semi": false, - "tabWidth": 2, - "singleQuote": true, - "printWidth": 100, - "trailingComma": "none" -} \ No newline at end of file diff --git a/examples/helia-vue/.vscode/extensions.json b/examples/helia-vue/.vscode/extensions.json deleted file mode 100644 index a7cea0b..0000000 --- a/examples/helia-vue/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["Vue.volar"] -} diff --git a/examples/helia-vue/index.html b/examples/helia-vue/index.html index 99f583a..f7e121f 100644 --- a/examples/helia-vue/index.html +++ b/examples/helia-vue/index.html @@ -2,7 +2,7 @@ - + Vite App diff --git a/examples/helia-vue/package.json b/examples/helia-vue/package.json index 023de06..ee3065f 100644 --- a/examples/helia-vue/package.json +++ b/examples/helia-vue/package.json @@ -3,38 +3,25 @@ "version": "0.0.0", "description": "Using Helia with vue", "private": true, + "type": "module", "scripts": { + "clean": "rimraf ./dist", + "dev": "vite", "start": "vite", "build": "vite build", "preview": "vite preview", - "test:e2e": "playwright test", - "test": "vite build && playwright test", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", - "format": "prettier --write src/" + "test": "npm run build && test-browser-example test" }, "dependencies": { - "@chainsafe/libp2p-noise": "^11.0.4", - "@chainsafe/libp2p-yamux": "^3.0.7", - "@helia/unixfs": "^1.2.1", - "@libp2p/bootstrap": "^6.0.3", - "@libp2p/websockets": "^5.0.8", - "blockstore-core": "^4.1.0", - "datastore-core": "^9.1.1", - "events": "^3.3.0", - "helia": "next", + "@helia/unixfs": "^1.4.1", + "helia": "^1.0.0", "it-all": "^3.0.1", - "libp2p": "^0.43.3", "vue": "^3.2.47" }, "devDependencies": { - "@playwright/test": "^1.31.1", - "@rushstack/eslint-patch": "^1.2.0", "@vitejs/plugin-vue": "^4.0.0", - "@vue/eslint-config-prettier": "^7.1.0", - "eslint": "^8.34.0", - "eslint-plugin-vue": "^9.9.0", - "prettier": "^2.8.4", - "test-util-ipfs-example": "^1.0.2", + "rimraf": "^5.0.0", + "test-ipfs-example": "^1.0.0", "vite": "^4.1.4" } } diff --git a/examples/helia-vue/playwright.config.js b/examples/helia-vue/playwright.config.js deleted file mode 100644 index ed1ac90..0000000 --- a/examples/helia-vue/playwright.config.js +++ /dev/null @@ -1,106 +0,0 @@ -// @ts-check -const { devices } = require('@playwright/test') - -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -// require('dotenv').config(); - -/** - * @see https://playwright.dev/docs/test-configuration - * @type {import('@playwright/test').PlaywrightTestConfig} - */ -const config = { - testDir: './e2e', - /* Maximum time one test can run for. */ - timeout: 30 * 1000, - expect: { - /** - * Maximum time expect() should wait for the condition to be met. - * For example in `await expect(locator).toHaveText();` - */ - timeout: 5000 - }, - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: Boolean(process.env.CI), - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ - actionTimeout: 0, - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: 'http://localhost:5173', - - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', - - /* Only on CI systems run the tests headless */ - headless: Boolean(process.env.CI) - }, - - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'] - } - }, - { - name: 'firefox', - use: { - ...devices['Desktop Firefox'] - } - }, - - /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { - // ...devices['Pixel 5'], - // }, - // }, - // { - // name: 'Mobile Safari', - // use: { - // ...devices['iPhone 12'], - // }, - // }, - - /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { - // channel: 'msedge', - // }, - // }, - // { - // name: 'Google Chrome', - // use: { - // channel: 'chrome', - // }, - // }, - ], - - /* Folder for test artifacts such as screenshots, videos, traces, etc. */ - // outputDir: 'test-results/', - - /* Run your local dev server before starting the tests */ - webServer: { - /** - * Use the dev server by default for faster feedback loop. - * Use the preview server on CI for more realistic testing. - */ - command: process.env.CI ? 'vite preview --port 5173' : 'vite dev', - port: 5173, - reuseExistingServer: !process.env.CI - } -} - -module.exports = config diff --git a/examples/helia-vue/public/favicon.ico b/examples/helia-vue/public/favicon.ico deleted file mode 100644 index df36fcf..0000000 Binary files a/examples/helia-vue/public/favicon.ico and /dev/null differ diff --git a/examples/helia-vue/src/HeliaApi/useCommitText.js b/examples/helia-vue/src/HeliaApi/useCommitText.js index 7c55a50..ee722ac 100644 --- a/examples/helia-vue/src/HeliaApi/useCommitText.js +++ b/examples/helia-vue/src/HeliaApi/useCommitText.js @@ -6,46 +6,45 @@ const encoder = new TextEncoder() const decoder = new TextDecoder() export const useCommitText = () => { - const cid = ref() - const commitedText = ref() - const { loading, error, helia, fs } = inject("HeliaProvider") + const cid = ref() + const commitedText = ref() + const { loading, error, helia, fs } = inject('HeliaProvider') - const commitText = async (text) => { - console.log('text', text) - if (error.value.length === 0 && !loading.value) { - try { - const res = await fs.value.addBytes( - encoder.encode(text), - helia.value.blockstore - ) - cid.value = res - } catch (e) { - console.error(e) - } - } else { - console.log('please wait for helia to start') - } + const commitText = async (text) => { + console.log('text', text) + if (error.value.length === 0 && !loading.value) { + try { + const res = await fs.value.addBytes( + encoder.encode(text), + helia.value.blockstore + ) + cid.value = res + } catch (e) { + console.error(e) + } + } else { + console.log('please wait for helia to start') } + } - const fetchCommitedText = async () => { - let text = '' - console.log(cid) - if (error.value.length === 0 && !loading.value && cid.value) { - try { - for await (const chunk of fs.value.cat(cid.value)) { - text += decoder.decode(chunk, { - stream: true - }) - } - commitedText.value = text - } catch (e) { - console.error(e) - } - } else { - console.log('please wait for helia to start') + const fetchCommitedText = async () => { + let text = '' + console.log(cid) + if (error.value.length === 0 && !loading.value && cid.value) { + try { + for await (const chunk of fs.value.cat(cid.value)) { + text += decoder.decode(chunk, { + stream: true + }) } - + commitedText.value = text + } catch (e) { + console.error(e) + } + } else { + console.log('please wait for helia to start') } + } - return {cid, commitText, commitedText, fetchCommitedText} -} \ No newline at end of file + return { cid, commitText, commitedText, fetchCommitedText } +} diff --git a/examples/helia-vue/src/HeliaApi/useUnixFS.js b/examples/helia-vue/src/HeliaApi/useUnixFS.js index 8bf027a..ddfec3a 100644 --- a/examples/helia-vue/src/HeliaApi/useUnixFS.js +++ b/examples/helia-vue/src/HeliaApi/useUnixFS.js @@ -6,99 +6,100 @@ const encoder = new TextEncoder() const decoder = new TextDecoder() export const useUnixFS = () => { - const { loading, error, fs } = inject("HeliaProvider") + const { loading, error, fs } = inject('HeliaProvider') - const getStat = async (dirCid, path) => { - if (error.value.length === 0 && !loading.value) { - try { - const res = await fs.value.stat(dirCid, { - path - }) - const data = { cid: res.cid, blocks: res.blocks } - return { status: 'success', data } - } catch (e) { - console.error(e) - } - } else { - console.log('please wait for helia to start') - } + const getStat = async (dirCid, path) => { + if (error.value.length === 0 && !loading.value) { + try { + const res = await fs.value.stat(dirCid, { + path + }) + const data = { cid: res.cid, blocks: res.blocks } + return { status: 'success', data } + } catch (e) { + console.error(e) + } + } else { + console.log('please wait for helia to start') } + } - const addDirectory = async (path) => { - if (error.value.length === 0 && !loading.value) { - try { - const emptyDirCid = await fs.value.addDirectory(path) - const res = await fs.value.mkdir(emptyDirCid, path) - return { status: 'success', data: res } - } catch (e) { - console.error(e) - } - } else { - console.log('please wait for helia to start') - } + const addDirectory = async (path) => { + if (error.value.length === 0 && !loading.value) { + try { + const emptyDirCid = await fs.value.addDirectory(path) + const res = await fs.value.mkdir(emptyDirCid, path) + return { status: 'success', data: res } + } catch (e) { + console.error(e) + } + } else { + console.log('please wait for helia to start') } + } - const getDirectory = async (dirCid, path) => { - const output = [] - if (error.value.length === 0 && !loading.value) { - try { - if (typeof path !== 'undefined') { - for await (const entry of fs.value.ls(dirCid, { - path - })) { - output.push(entry) - } - } else { - for await (const entry of fs.value.ls(dirCid)) { - output.push(entry) - } - } - return { status: 'success', data: output } - } catch (e) { - console.error(e) - } + const getDirectory = async (dirCid, path) => { + const output = [] + if (error.value.length === 0 && !loading.value) { + try { + if (typeof path !== 'undefined') { + for await (const entry of fs.value.ls(dirCid, { + path + })) { + output.push(entry) + } } else { - console.log('please wait for helia to start') + for await (const entry of fs.value.ls(dirCid)) { + output.push(entry) + } } + return { status: 'success', data: output } + } catch (e) { + console.error(e) + } + } else { + console.log('please wait for helia to start') } + } - const addFile = async (name, dirCid, content) => { - if (error.value.length === 0 && !loading.value) { - try { - - const res = await fs.value.addFile({ - content: encoder.encode(content) - }) - const updatedCid = await fs.value.cp(res, dirCid, name) - return { status: 'success', data: { dirCid: updatedCid, fileCid: res } } - } catch (e) { - console.error(e) - } - } else { - console.log('please wait for helia to start') - } + const addFile = async (name, dirCid, content) => { + if (error.value.length === 0 && !loading.value) { + try { + const res = await fs.value.addFile({ + content: encoder.encode(content) + }) + const updatedCid = await fs.value.cp(res, dirCid, name) + return { status: 'success', data: { dirCid: updatedCid, fileCid: res } } + } catch (e) { + console.error(e) + } + } else { + console.log('please wait for helia to start') } + } - const getFile = async (cid) => { - if (error.value.length === 0 && !loading.value) { - let txt = '' - try { - for await (const buf of fs.value.cat(cid)) { - txt += decoder.decode(buf, { - stream: true - }) - } - return { status: 'success', data: txt } - } catch (e) { - console.error(e) - } - } else { - console.log('please wait for helia to start') + const getFile = async (cid) => { + if (error.value.length === 0 && !loading.value) { + let txt = '' + try { + for await (const buf of fs.value.cat(cid)) { + txt += decoder.decode(buf, { + stream: true + }) } + return { status: 'success', data: txt } + } catch (e) { + console.error(e) + } + } else { + console.log('please wait for helia to start') } - return { - getStat, - addDirectory, getDirectory, - addFile, getFile - } -} \ No newline at end of file + } + return { + getStat, + addDirectory, + getDirectory, + addFile, + getFile + } +} diff --git a/examples/helia-vue/src/main.js b/examples/helia-vue/src/main.js index 7c1a182..76a1d7a 100644 --- a/examples/helia-vue/src/main.js +++ b/examples/helia-vue/src/main.js @@ -1,9 +1,9 @@ import { createApp } from 'vue' -import App from '@/App.vue' import { HeliaProviderPlugin } from './plugins/HeliaProviderPlugin' +import App from '@/App.vue' import './assets/main.css' const app = createApp(App) app.use(HeliaProviderPlugin) -app.mount("#app") +app.mount('#app') diff --git a/examples/helia-vue/src/plugins/HeliaProviderPlugin.js b/examples/helia-vue/src/plugins/HeliaProviderPlugin.js index 6029bf1..5a4efcd 100644 --- a/examples/helia-vue/src/plugins/HeliaProviderPlugin.js +++ b/examples/helia-vue/src/plugins/HeliaProviderPlugin.js @@ -1,21 +1,13 @@ /* eslint-disable no-console */ -import { createHelia } from 'helia' -import { createLibp2p } from 'libp2p' -import { noise } from '@chainsafe/libp2p-noise' -import { yamux } from '@chainsafe/libp2p-yamux' -import { webSockets } from '@libp2p/websockets' -import { bootstrap } from '@libp2p/bootstrap' import { unixfs } from '@helia/unixfs' -import { MemoryBlockstore } from 'blockstore-core' -import { MemoryDatastore } from 'datastore-core' - +import { createHelia } from 'helia' import { ref } from 'vue' export const HeliaProviderPlugin = { install: async (app) => { const loading = ref(true) - const error = ref("") + const error = ref('') const helia = ref() const fs = ref() app.provide('HeliaProvider', { @@ -25,39 +17,10 @@ export const HeliaProviderPlugin = { fs }) try { - const blockstore = new MemoryBlockstore() - const datastore = new MemoryDatastore() - const libp2p = await createLibp2p({ - datastore, - transports: [ - webSockets() - ], - connectionEncryption: [ - noise() - ], - streamMuxers: [ - yamux() - ], - peerDiscovery: [ - bootstrap({ - list: [ - "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", - "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", - "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", - "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt" - ] - }) - ] - }) - const instance = await createHelia({ - datastore, - blockstore, - libp2p - }) + const instance = await createHelia() loading.value = false helia.value = instance fs.value = unixfs(instance) - } catch (e) { console.error(e) error.value = e.toString() diff --git a/examples/helia-vue/e2e/vue.spec.js b/examples/helia-vue/test/index.spec.js similarity index 78% rename from examples/helia-vue/e2e/vue.spec.js rename to examples/helia-vue/test/index.spec.js index d00398a..3e49da8 100644 --- a/examples/helia-vue/e2e/vue.spec.js +++ b/examples/helia-vue/test/index.spec.js @@ -1,16 +1,14 @@ -import { playwright } from 'test-util-ipfs-example' -const { test, expect } = require('@playwright/test'); +import { setup, expect } from 'test-ipfs-example/browser' -const play = test.extend({ - ...playwright.servers() -}) +// Setup +const test = setup() -play.describe("Use Helia with Vue", () => { - play.beforeEach(async ({ servers, page }) => { - await page.goto(`http://localhost:${servers[0].port}/`) +test.describe('Use Helia with Vue', () => { + test.beforeEach(async ({ servers, page }) => { + await page.goto(servers[0].url) }) - play('should properly initialize Helia, and add/get a file', async ({ page }) => { + test('should properly initialize Helia, and add/get a file', async ({ page }) => { const exampleText = 'Hello Helia' const exampleDirName = 'newdir' const exampleFileName = 'test.txt' @@ -19,7 +17,7 @@ play.describe("Use Helia with Vue", () => { await expect(status).toHaveCSS( 'background-color', 'rgb(0, 128, 0)', - {timeout: 5000} + { timeout: 5000 } ) await page.fill('#commitText', exampleText) @@ -27,14 +25,14 @@ play.describe("Use Helia with Vue", () => { const cidOutput = page.locator('#commitTextCidOutput') await expect(cidOutput).toHaveText( 'cid: bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy', - {timeout: 2000} + { timeout: 2000 } ) await page.click('#fetchCommitedTextButton') const textOutput = page.locator('#fetchedCommitedTextOutput') await expect(textOutput).toHaveText( `added text: ${exampleText}`, - {timeout: 2000} + { timeout: 2000 } ) await page.fill('#newDirInput', exampleDirName) @@ -42,21 +40,21 @@ play.describe("Use Helia with Vue", () => { const dirOutput = page.locator('#newDirOutput') await expect(dirOutput).toHaveText( 'directory Cid: bafybeif5hfzip34o7ocwfg4ge537g7o3nbh3auc3s54l3gsaantucqyyra', - {timeout: 2000} + { timeout: 2000 } ) - await page.click("#statDirButton") + await page.click('#statDirButton') const statDir = page.locator('#statDirOutput') await expect(statDir).toContainText( 'bafybeiczsscdsbs7ffqz55asqdf3smv6klcw3gofszvwlyarci47bgf354', - {timeout: 2000} + { timeout: 2000 } ) await page.click('#getDirButton') const lsDir = await page.locator('#dirListOutput') await expect(lsDir).toContainText( 'bafybeif5hfzip34o7ocwfg4ge537g7o3nbh3auc3s54l3gsaantucqyyra/newdir', - {timeout: 2000} + { timeout: 2000 } ) await page.fill('#fileNameInput', exampleFileName) @@ -65,28 +63,25 @@ play.describe("Use Helia with Vue", () => { const fileCidOutput = page.locator('#fileCidOutput') await expect(fileCidOutput).toContainText( 'bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy', - {timeout: 2000} + { timeout: 2000 } ) const updatedDirOutput = page.locator('#updatedDirOutput') await expect(updatedDirOutput).toContainText( 'bafybeie6lhtnjvea4j7imyx5lp2c7kfgxjkndc4jjpmcetnkq75lapmgwm', - {timeout: 2000} + { timeout: 2000 } ) await page.click('#dirContentsButton') const dirContentsOutput = page.locator('#dirContentsOutput') await expect(dirContentsOutput).toContainText( 'bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy', - {timeout: 2000} + { timeout: 2000 } ) await page.click('#fileContentsButton') const fileContentsOutput = page.locator('#fileContentsOutput') await expect(fileContentsOutput).toContainText( exampleText, - {timeout: 2000} + { timeout: 2000 } ) - }) }) - - diff --git a/examples/helia-vue/vite.config.js b/examples/helia-vue/vite.config.js index de5cb31..922bb8d 100644 --- a/examples/helia-vue/vite.config.js +++ b/examples/helia-vue/vite.config.js @@ -1,7 +1,6 @@ import { fileURLToPath, URL } from 'node:url' - -import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' // https://vitejs.dev/config/ export default defineConfig({ diff --git a/examples/helia-webpack/package.json b/examples/helia-webpack/package.json index 2488362..09b5d77 100644 --- a/examples/helia-webpack/package.json +++ b/examples/helia-webpack/package.json @@ -11,21 +11,14 @@ "build": "webpack --env production", "serve": "webpack serve --mode=development", "start": "npm run serve", - "test": "npm run build && playwright test tests" + "test": "npm run build && test-browser-example test" }, "browserslist": [ "last 1 Chrome version" ], "dependencies": { - "@chainsafe/libp2p-noise": "^11.0.1", - "@chainsafe/libp2p-yamux": "^3.0.5", - "@helia/unixfs": "^1.0.5", - "@libp2p/bootstrap": "^6.0.0", - "@libp2p/websockets": "^5.0.3", - "blockstore-core": "^4.0.1", - "datastore-core": "^9.0.3", + "@helia/unixfs": "^1.4.1", "helia": "^1.0.0", - "libp2p": "^0.43.3", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -40,9 +33,9 @@ "html-webpack-plugin": "^5.3.2", "node-polyfill-webpack-plugin": "^2.0.1", "playwright": "^1.12.3", - "rimraf": "^4.1.2", + "rimraf": "^5.0.0", "style-loader": "^3.1.0", - "test-util-ipfs-example": "^1.0.2", + "test-ipfs-example": "^1.0.0", "webpack": "^5.45.1", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.6.0", diff --git a/examples/helia-webpack/public/favicon.ico b/examples/helia-webpack/public/favicon.ico deleted file mode 100644 index b2f1f96..0000000 Binary files a/examples/helia-webpack/public/favicon.ico and /dev/null differ diff --git a/examples/helia-webpack/public/index.html b/examples/helia-webpack/public/index.html index 97483b2..2d55b01 100644 --- a/examples/helia-webpack/public/index.html +++ b/examples/helia-webpack/public/index.html @@ -11,7 +11,8 @@ rel="stylesheet" href="https://unpkg.com/tachyons@4.10.0/css/tachyons.min.css" /> - + +
diff --git a/examples/helia-webpack/src/app.js b/examples/helia-webpack/src/app.js index 8111f43..5a121cc 100644 --- a/examples/helia-webpack/src/app.js +++ b/examples/helia-webpack/src/app.js @@ -1,7 +1,6 @@ -import React, { useState, useRef } from 'react' -import ipfsLogo from './ipfs-logo.svg' -import { getHelia } from './get-helia.js' import { unixfs } from '@helia/unixfs' +import { createHelia } from 'helia' +import React, { useState, useRef } from 'react' function App () { const [output, setOutput] = useState([]) @@ -37,7 +36,7 @@ function App () { if (!helia) { showStatus('Creating Helia node...', COLORS.active) - node = await getHelia() + node = await createHelia() setHelia(node) } @@ -91,17 +90,22 @@ function App () { return ( <> -
- - IPFS logo +
+ + Helia logo
-

Add data to Helia from the browser

+

Add data to Helia

- + setFileName(e.target.value)} /> - + - - , - document.getElementById('root') -) +const container = document.getElementById('root') +const root = createRoot(container) +root.render() diff --git a/examples/helia-webpack/tests/test.js b/examples/helia-webpack/test/index.spec.js similarity index 69% rename from examples/helia-webpack/tests/test.js rename to examples/helia-webpack/test/index.spec.js index 36b60c1..d90aad3 100644 --- a/examples/helia-webpack/tests/test.js +++ b/examples/helia-webpack/test/index.spec.js @@ -1,23 +1,20 @@ -import { test, expect } from '@playwright/test' -import { playwright } from 'test-util-ipfs-example' +import { setup, expect } from 'test-ipfs-example/browser' // Setup -const play = test.extend({ - ...playwright.servers() -}) +const test = setup() -play.describe('bundle Helia with Webpack:', () => { +test.describe('bundle Helia with Webpack:', () => { // DOM const nameInput = '#file-name' const contentInput = '#file-content' const submitBtn = '#add-submit' const output = '#output' - play.beforeEach(async ({ servers, page }) => { - await page.goto(`http://localhost:${servers[0].port}/`) + test.beforeEach(async ({ servers, page }) => { + await page.goto(servers[0].url) }) - play('should properly initialized a Helia node and add/get a file', async ({ page }) => { + test('should properly initialized a Helia node and add/get a file', async ({ page }) => { const fileName = 'test.txt' const stringToUse = 'Hello world!' diff --git a/examples/helia-webpack/webpack.config.js b/examples/helia-webpack/webpack.config.js index 0452d5e..af80286 100644 --- a/examples/helia-webpack/webpack.config.js +++ b/examples/helia-webpack/webpack.config.js @@ -1,10 +1,10 @@ import path from 'path' -import webpack from 'webpack' -import { merge } from 'webpack-merge' import { fileURLToPath } from 'url' import CopyWebpackPlugin from 'copy-webpack-plugin' -import NodePolyfillPlugin from 'node-polyfill-webpack-plugin' import HtmlWebpackPlugin from 'html-webpack-plugin' +import NodePolyfillPlugin from 'node-polyfill-webpack-plugin' +import webpack from 'webpack' +import { merge } from 'webpack-merge' const __dirname = path.dirname(fileURLToPath(import.meta.url)) @@ -26,6 +26,9 @@ const paths = { public: path.resolve(__dirname, './public') } +/** + * @type {Partial} + */ const prod = { mode: 'production', devtool: false, @@ -43,6 +46,9 @@ const prod = { target: 'browserslist' } +/** + * @type {Partial} + */ const dev = { // Set the mode to development or production mode: 'development', @@ -73,6 +79,9 @@ const dev = { ] } +/** + * @type {Partial} + */ const common = { // Where webpack looks to start building the bundle entry: [paths.src + '/index.js'], @@ -108,7 +117,6 @@ const common = { // Generates deprecation warning: https://github.com/jantimon/html-webpack-plugin/issues/1501 new HtmlWebpackPlugin({ title: 'Helia bundle by Webpack', - favicon: paths.public + '/favicon.ico', template: paths.public + '/index.html', // template file filename: 'index.html', // output file, minify: false diff --git a/package.json b/package.json index cdae5f2..6d7d5dc 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ }, "homepage": "https://github.com/ipfs-examples/helia-examples#readme", "devDependencies": { - "aegir": "^38.1.0", + "aegir": "^39.0.6", + "eslint-config-ipfs": "^4.0.3", "eslint-plugin-react": "^7.32.2" }, "workspaces": [ @@ -43,5 +44,8 @@ "version": "detect" } } + }, + "@parcel/resolver-default": { + "packageExports": true } }