diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78e62be3..098bcdc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,7 @@ jobs: - browser-add-readable-stream - browser-angular - browser-create-react-app + - browser-esbuild - browser-exchange-files - browser-ipns-publish - browser-lit diff --git a/examples/browser-esbuild/.github/pull_request_template.md b/examples/browser-esbuild/.github/pull_request_template.md new file mode 100644 index 00000000..e6a8f900 --- /dev/null +++ b/examples/browser-esbuild/.github/pull_request_template.md @@ -0,0 +1,15 @@ +**IMPORTANT: Please do not create a Pull Request for this repository.** + +The contents of this repository are automatically synced from the parent [IPFS Examples Project](https://github.com/ipfs-examples/js-ipfs-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/js-ipfs-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 [IPFS Examples Project](https://github.com/ipfs-examples/js-ipfs-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/browser-esbuild/.github/workflows/sync.yml b/examples/browser-esbuild/.github/workflows/sync.yml new file mode 100644 index 00000000..bdcb1fc6 --- /dev/null +++ b/examples/browser-esbuild/.github/workflows/sync.yml @@ -0,0 +1,20 @@ +name: Sync +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + +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/js-ipfs-examples" + source-folder-path: "examples/browser-esbuild" + source-branch: "master" + target-branch: "main" + git-username: github-actions + git-email: github-actions@github.com diff --git a/examples/browser-esbuild/README.md b/examples/browser-esbuild/README.md new file mode 100644 index 00000000..a28c4dae --- /dev/null +++ b/examples/browser-esbuild/README.md @@ -0,0 +1,122 @@ +

+ + IPFS in JavaScript logo + +

+ +

js-ipfs with esbuild

+ +

+ Bundle js-ipfs with esbuil! +
+
+ +
+ 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) +- [References](#references) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [Want to hack on IPFS?](#want-to-hack-on-ipfs) + +## About The Project + +- Read the [docs](https://github.com/ipfs/js-ipfs/tree/master/docs) +- Look into other [examples](https://github.com/ipfs-examples/js-ipfs-examples) to learn how to spawn an IPFS node in Node.js and in the Browser +- Consult the [Core API docs](https://github.com/ipfs/js-ipfs/tree/master/docs/core-api) to see what you can do with an IPFS node +- 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 [esbuild](https://esbuild.github.io/), so that you can use it in your own web app! + +You should see the following: + +![](./img/1.png) +![](./img/2.png) + +This example demonstrates the `Regular API`, top-level API for add, cat, get and ls Files on IPFS + +_For more examples, please refer to the [Documentation](#documentation)_ + +## References + +- Documentation: + - [IPFS CONFIG](https://github.com/ipfs/js-ipfs/blob/master/docs/CONFIG.md) + - [MISCELLANEOUS](https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/MISCELLANEOUS.md) + - [FILES](https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/FILES.md) +- Tutorials: + - [MFS API](https://proto.school/mutable-file-system) + - [Regular File API](https://proto.school/regular-files-api) + +## Documentation + +- [Config](https://docs.ipfs.io/) +- [Core API](https://github.com/ipfs/js-ipfs/tree/master/docs/core-api) +- [Examples](https://github.com/ipfs-examples/js-ipfs-examples) +- [Development](https://github.com/ipfs/js-ipfs/blob/master/docs/DEVELOPMENT.md) +- [Tutorials](https://proto.school) + +## 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/js-ipfs/issues) has many that are marked as ['help wanted'](https://github.com/ipfs/js-ipfs/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22help+wanted%22) or ['difficulty:easy'](https://github.com/ipfs/js-ipfs/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 [IPFS Roadmap](https://github.com/ipfs/roadmap)** 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. +- **Join the [Weekly Core Implementations Call](https://github.com/ipfs/team-mgmt/issues/992)** it's where everyone discusses what's going on with IPFS and what's next diff --git a/examples/browser-esbuild/esbuild.js b/examples/browser-esbuild/esbuild.js new file mode 100644 index 00000000..b236c897 --- /dev/null +++ b/examples/browser-esbuild/esbuild.js @@ -0,0 +1,14 @@ +import { build } from 'esbuild' + +build({ + entryPoints: ['./src/index.js'], + outfile: './dist/index.js', + sourcemap: 'external', + minify: true, + bundle: true, + define: { + 'process.env.NODE_DEBUG': 'false', + 'global': 'globalThis' + } +}) + .catch(() => process.exit(1)) \ No newline at end of file diff --git a/examples/browser-esbuild/favicon.ico b/examples/browser-esbuild/favicon.ico new file mode 100644 index 00000000..b2f1f968 Binary files /dev/null and b/examples/browser-esbuild/favicon.ico differ diff --git a/examples/browser-esbuild/img/1.png b/examples/browser-esbuild/img/1.png new file mode 100644 index 00000000..1bf37bd6 Binary files /dev/null and b/examples/browser-esbuild/img/1.png differ diff --git a/examples/browser-esbuild/img/2.png b/examples/browser-esbuild/img/2.png new file mode 100644 index 00000000..83afeb86 Binary files /dev/null and b/examples/browser-esbuild/img/2.png differ diff --git a/examples/browser-esbuild/package.json b/examples/browser-esbuild/package.json new file mode 100644 index 00000000..68b24da8 --- /dev/null +++ b/examples/browser-esbuild/package.json @@ -0,0 +1,30 @@ +{ + "name": "example-browser-esbuild", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "Bundle js-ipfs with esbuild", + "keywords": [], + "license": "ISC", + "author": "", + "scripts": { + "clean": "rimraf ./dist", + "build": "mkdir -p dist && cp src/index.html src/style.css src/ipfs-logo.svg dist && node esbuild.js", + "start": "npm run build && esbuild --servedir=dist", + "test": "npm run build && playwright test tests" + }, + "browserslist": "last 1 Chrome version", + "dependencies": { + "ipfs-core": "^0.16.0" + }, + "devDependencies": { + "@babel/core": "^7.14.8", + "@playwright/test": "^1.12.3", + "esbuild": "^0.15.7", + "playwright": "^1.12.3", + "process": "^0.11.10", + "rimraf": "^3.0.2", + "test-util-ipfs-example": "^1.0.2", + "util": "^0.12.4" + } +} diff --git a/examples/browser-esbuild/src/index.html b/examples/browser-esbuild/src/index.html new file mode 100644 index 00000000..c2f48ab0 --- /dev/null +++ b/examples/browser-esbuild/src/index.html @@ -0,0 +1,87 @@ + + + + + + + Bundle js-ipfs with parcel.js + + + + + + + + + +
+ + IPFS logo + +
+ +
+

Add data to IPFS from the browser

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

Output

+ +
+
+
+
+
+ + diff --git a/examples/browser-esbuild/src/index.js b/examples/browser-esbuild/src/index.js new file mode 100644 index 00000000..7e338590 --- /dev/null +++ b/examples/browser-esbuild/src/index.js @@ -0,0 +1,117 @@ +import { create } from 'ipfs-core' + +const App = () => { + let ipfs + + 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') + } + + 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) => { + let log = DOM.output() + + if (!log) { + const output = document.createElement('div') + output.id = 'output' + DOM.terminal().appendChild(output) + + log = DOM.output() + } + + const line = document.createElement('p') + line.innerText = 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 ipfs.cat(cid)) { + content += decoder.decode(chunk, { + stream: true + }) + } + + return content + } + + const store = async (name, content) => { + if (!ipfs) { + showStatus('Creating IPFS node...', COLORS.active) + + ipfs = await create({ + repo: String(Math.random() + Date.now()), + init: { alogorithm: 'ed25519' } + }) + } + + const id = await ipfs.id() + showStatus(`Connecting to ${id.id}...`, COLORS.active, id.id) + + const fileToAdd = { + path: `${name}`, + content: content + } + + showStatus(`Adding file ${fileToAdd.path}...`, COLORS.active) + const file = await ipfs.add(fileToAdd) + + showStatus(`Added to ${file.cid}`, COLORS.success, file.cid) + + showStatus('Reading file...', COLORS.active) + + const text = await cat(file.cid) + + showStatus(`\u2514\u2500 ${file.path} ${text.toString()}`) + showStatus(`Preview: https://ipfs.io/ipfs/${file.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) + } + } +} + +App() diff --git a/examples/browser-esbuild/src/ipfs-logo.svg b/examples/browser-esbuild/src/ipfs-logo.svg new file mode 100644 index 00000000..f8633f62 --- /dev/null +++ b/examples/browser-esbuild/src/ipfs-logo.svg @@ -0,0 +1 @@ + diff --git a/examples/browser-esbuild/src/style.css b/examples/browser-esbuild/src/style.css new file mode 100644 index 00000000..1c9bd170 --- /dev/null +++ b/examples/browser-esbuild/src/style.css @@ -0,0 +1,42 @@ +::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; +} diff --git a/examples/browser-esbuild/tests/test.js b/examples/browser-esbuild/tests/test.js new file mode 100644 index 00000000..b6ade142 --- /dev/null +++ b/examples/browser-esbuild/tests/test.js @@ -0,0 +1,37 @@ +import { test, expect } from '@playwright/test'; +import { playwright } from 'test-util-ipfs-example'; + +// Setup +const play = test.extend({ + ...playwright.servers(), +}); + +play.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}/`); + }) + + play('should properly initialized a IPFS node and add/get a file', async ({ page }) => { + const fileName = 'test.txt' + const stringToUse = 'Hello world!' + + await page.fill(nameInput, fileName) + await page.fill(contentInput, stringToUse) + await page.click(submitBtn) + + await page.waitForSelector(`${output}:has-text("/QmQzCQn4puG4qu8PVysxZmscmQ5vT1ZXpqo7f58Uh9QfyY")`) + + const outputContent = await page.textContent(output) + + expect(outputContent).toContain("QmQzCQn4puG4qu8PVysxZmscmQ5vT1ZXpqo7f58Uh9QfyY"); + expect(outputContent).toContain("https://ipfs.io/ipfs/QmQzCQn4puG4qu8PVysxZmscmQ5vT1ZXpqo7f58Uh9QfyY"); + expect(outputContent).toContain(fileName); + expect(outputContent).toContain(stringToUse); + }); +}); diff --git a/examples/browser-vite/README.md b/examples/browser-vite/README.md index d1ca7afb..01627c54 100644 --- a/examples/browser-vite/README.md +++ b/examples/browser-vite/README.md @@ -4,10 +4,10 @@

-

js-ipfs with parcel.js

+

js-ipfs with vite

- Bundle js-ipfs with ParcelJS! + Bundle js-ipfs with vite!