From c7cad0006541ef4f8b3dc9469e4cae4e3ea4747b Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Mon, 11 Mar 2024 19:00:26 -0700 Subject: [PATCH 01/20] feat: allow explicit sw de-registering --- src/components/config.tsx | 2 +- src/context/service-worker-context.tsx | 13 ++++++++++++- src/lib/deregister-request.ts | 16 ++++++++++++++++ src/sw.ts | 25 ++++++++++++++++++++++++- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/lib/deregister-request.ts diff --git a/src/components/config.tsx b/src/components/config.tsx index 80a0ed50..c70b2ba7 100644 --- a/src/components/config.tsx +++ b/src/components/config.tsx @@ -47,7 +47,7 @@ export default (): JSX.Element | null => { // we get the iframe origin from a query parameter called 'origin', if this is loaded in an iframe const targetOrigin = decodeURIComponent(window.location.hash.split('@origin=')[1]) const config = await getConfig() - trace('config-page: postMessage config to origin ', config, origin) + trace('config-page: postMessage config to origin ', config, targetOrigin) /** * The reload page in the parent window is listening for this message, and then it passes a RELOAD_CONFIG message to the service worker */ diff --git a/src/context/service-worker-context.tsx b/src/context/service-worker-context.tsx index dce50eb0..a49a2236 100644 --- a/src/context/service-worker-context.tsx +++ b/src/context/service-worker-context.tsx @@ -13,8 +13,9 @@ * 1. The page being loaded using some /ip[fn]s/ url, but subdomain isolation is supported, so we need to redirect to the isolated origin */ import React, { createContext, useEffect, useState } from 'react' +import { getRedirectUrl, isDeregisterRequest } from '../lib/deregister-request.ts' import { translateIpfsRedirectUrl } from '../lib/ipfs-hosted-redirect-utils.ts' -import { error } from '../lib/logger.ts' +import { error, trace } from '../lib/logger.ts' import { findOriginIsolationRedirect } from '../lib/path-or-subdomain.ts' import { registerServiceWorker } from '../service-worker-utils.ts' @@ -54,6 +55,16 @@ export const ServiceWorkerProvider = ({ children }): JSX.Element => { return } async function doWork (): Promise { + if (isDeregisterRequest(window.location.href)) { + trace('UI: deregistering service worker') + const registration = await navigator.serviceWorker.getRegistration() + if (registration != null) { + await registration.unregister() + window.location.replace(getRedirectUrl(window.location.href).href) + } else { + error('UI: service worker not registered, cannot deregister') + } + } const registration = await navigator.serviceWorker.getRegistration() if (registration != null) { diff --git a/src/lib/deregister-request.ts b/src/lib/deregister-request.ts new file mode 100644 index 00000000..fb8ed164 --- /dev/null +++ b/src/lib/deregister-request.ts @@ -0,0 +1,16 @@ +import { trace } from './logger.ts' + +// things are wonky with hash routes for deregistering. +export function isDeregisterRequest (url: string): boolean { + const urlObj = new URL(url) + const result = urlObj.search.includes('ipfs-sw-deregister') + trace('isDeregisterRequest: ', url, result) + return result +} + +export function getRedirectUrl (url: string): URL { + const redirectUrl = new URL(url) + redirectUrl.search = '' + redirectUrl.hash = '#/ipfs-sw-config' + return redirectUrl +} diff --git a/src/sw.ts b/src/sw.ts index 029a48e2..971daee4 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -3,6 +3,7 @@ import { createVerifiedFetch, type VerifiedFetch } from '@helia/verified-fetch' import { HeliaServiceWorkerCommsChannel, type ChannelMessage } from './lib/channel.ts' import { getConfig } from './lib/config-db.ts' import { contentTypeParser } from './lib/content-type-parser.ts' +import { getRedirectUrl, isDeregisterRequest } from './lib/deregister-request.ts' import { getSubdomainParts } from './lib/get-subdomain-parts.ts' import { isConfigPage } from './lib/is-config-page.ts' import { error, log, trace } from './lib/logger.ts' @@ -86,7 +87,10 @@ self.addEventListener('fetch', (event) => { const urlString = request.url const url = new URL(urlString) - if (isConfigPageRequest(url) || isSwAssetRequest(event)) { + if (isDeregisterRequest(event.request.url)) { + event.waitUntil(deregister(event)) + return + } else if (isConfigPageRequest(url) || isSwAssetRequest(event)) { // get the assets from the server trace('helia-sw: config page or js asset request, ignoring ', urlString) return @@ -125,6 +129,25 @@ async function getVerifiedFetch (): Promise { return verifiedFetch } +// potential race condition +async function deregister (event): Promise { + if (!isSubdomainRequest(event)) { + // if we are at the root, we need to ignore this request due to race conditions with the UI + return + } + await self.registration.unregister() + const clients = await self.clients.matchAll({ type: 'window' }) + + for (const client of clients) { + const newUrl = getRedirectUrl(client.url) + try { + await client.navigate(newUrl) + } catch (e) { + error('error navigating client to ', newUrl, e) + } + } +} + function isRootRequestForContent (event: FetchEvent): boolean { const urlIsPreviouslyIntercepted = urlInterceptRegex.some(regex => regex.test(event.request.url)) const isRootRequest = urlIsPreviouslyIntercepted From f6a382be916c2bad17312f3e49202c6dca1e7482 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:49:28 -0700 Subject: [PATCH 02/20] fix: tsc generates js files --- src/app.tsx | 12 ++++++------ src/components/Header.tsx | 2 +- src/components/TerminalOutput.tsx | 2 +- src/components/config.tsx | 16 ++++++++-------- src/components/sw-ready-button.tsx | 2 +- src/components/types.ts | 2 +- src/context/config-context.tsx | 2 +- src/context/service-worker-context.tsx | 10 +++++----- src/helper-ui.tsx | 8 ++++---- src/index.tsx | 10 +++++----- src/lib/channel.ts | 4 ++-- src/lib/config-db.ts | 2 +- src/lib/deregister-request.ts | 2 +- src/lib/get-subdomain-parts.ts | 2 +- src/lib/path-or-subdomain.ts | 2 +- src/redirectPage.tsx | 12 ++++++------ src/service-worker-utils.ts | 2 +- src/sw.ts | 16 ++++++++-------- tsconfig.json | 6 +++--- 19 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/app.tsx b/src/app.tsx index a1c1775c..a98530ee 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,10 +1,10 @@ import React, { useContext } from 'react' -import Config from './components/config.tsx' -import { ConfigContext } from './context/config-context.tsx' -import HelperUi from './helper-ui.tsx' -import { isConfigPage } from './lib/is-config-page.ts' -import { isPathOrSubdomainRequest } from './lib/path-or-subdomain.ts' -import RedirectPage from './redirectPage.tsx' +import Config from './components/config.jsx' +import { ConfigContext } from './context/config-context.jsx' +import HelperUi from './helper-ui.jsx' +import { isConfigPage } from './lib/is-config-page.js' +import { isPathOrSubdomainRequest } from './lib/path-or-subdomain.js' +import RedirectPage from './redirectPage.jsx' function App (): JSX.Element { const { isConfigExpanded, setConfigExpanded } = useContext(ConfigContext) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 529a34ff..0e74f9a7 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react' -import { ConfigContext } from '../context/config-context.tsx' +import { ConfigContext } from '../context/config-context.jsx' import gearIcon from '../gear-icon.svg' import ipfsLogo from '../ipfs-logo.svg' diff --git a/src/components/TerminalOutput.tsx b/src/components/TerminalOutput.tsx index d52f69a2..84924828 100644 --- a/src/components/TerminalOutput.tsx +++ b/src/components/TerminalOutput.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react' -import type { OutputLine } from './types.ts' +import type { OutputLine } from './types.js' interface TerminalOutputProps { output: OutputLine[] diff --git a/src/components/config.tsx b/src/components/config.tsx index c70b2ba7..87f6204c 100644 --- a/src/components/config.tsx +++ b/src/components/config.tsx @@ -1,13 +1,13 @@ import React, { useCallback, useContext, useEffect, useState } from 'react' -import { ConfigContext } from '../context/config-context.tsx' -import { HeliaServiceWorkerCommsChannel } from '../lib/channel.ts' -import { getConfig, loadConfigFromLocalStorage } from '../lib/config-db.ts' -import { LOCAL_STORAGE_KEYS } from '../lib/local-storage.ts' -import { trace } from '../lib/logger.ts' -import { Collapsible } from './collapsible.tsx' -import LocalStorageInput from './local-storage-input.tsx' +import { ConfigContext } from '../context/config-context.jsx' +import { HeliaServiceWorkerCommsChannel } from '../lib/channel.js' +import { getConfig, loadConfigFromLocalStorage } from '../lib/config-db.js' +import { LOCAL_STORAGE_KEYS } from '../lib/local-storage.js' +import { trace } from '../lib/logger.js' +import { Collapsible } from './collapsible.jsx' +import LocalStorageInput from './local-storage-input.jsx' import { LocalStorageToggle } from './local-storage-toggle' -import { ServiceWorkerReadyButton } from './sw-ready-button.tsx' +import { ServiceWorkerReadyButton } from './sw-ready-button.jsx' const channel = new HeliaServiceWorkerCommsChannel('WINDOW') diff --git a/src/components/sw-ready-button.tsx b/src/components/sw-ready-button.tsx index d3b3f5b2..c74f824e 100644 --- a/src/components/sw-ready-button.tsx +++ b/src/components/sw-ready-button.tsx @@ -1,5 +1,5 @@ import React, { useContext, useMemo } from 'react' -import { ServiceWorkerContext } from '../context/service-worker-context.tsx' +import { ServiceWorkerContext } from '../context/service-worker-context.jsx' type ButtonProps = JSX.IntrinsicElements['button'] interface ServiceWorkerReadyButtonProps extends ButtonProps { diff --git a/src/components/types.ts b/src/components/types.ts index 73a5e4c6..53c8dd38 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -1,4 +1,4 @@ -import type { COLORS } from '../lib/common.ts' +import type { COLORS } from '../lib/common.js' export interface OutputLine { content: string diff --git a/src/context/config-context.tsx b/src/context/config-context.tsx index a3237a1d..98ea3d4b 100644 --- a/src/context/config-context.tsx +++ b/src/context/config-context.tsx @@ -1,5 +1,5 @@ import React, { createContext, useState } from 'react' -import { isConfigPage } from '../lib/is-config-page.ts' +import { isConfigPage } from '../lib/is-config-page.js' const isLoadedInIframe = window.self !== window.top diff --git a/src/context/service-worker-context.tsx b/src/context/service-worker-context.tsx index a49a2236..3a82d6dc 100644 --- a/src/context/service-worker-context.tsx +++ b/src/context/service-worker-context.tsx @@ -13,11 +13,11 @@ * 1. The page being loaded using some /ip[fn]s/ url, but subdomain isolation is supported, so we need to redirect to the isolated origin */ import React, { createContext, useEffect, useState } from 'react' -import { getRedirectUrl, isDeregisterRequest } from '../lib/deregister-request.ts' -import { translateIpfsRedirectUrl } from '../lib/ipfs-hosted-redirect-utils.ts' -import { error, trace } from '../lib/logger.ts' -import { findOriginIsolationRedirect } from '../lib/path-or-subdomain.ts' -import { registerServiceWorker } from '../service-worker-utils.ts' +import { getRedirectUrl, isDeregisterRequest } from '../lib/deregister-request.js' +import { translateIpfsRedirectUrl } from '../lib/ipfs-hosted-redirect-utils.js' +import { error, trace } from '../lib/logger.js' +import { findOriginIsolationRedirect } from '../lib/path-or-subdomain.js' +import { registerServiceWorker } from '../service-worker-utils.js' export const ServiceWorkerContext = createContext({ isServiceWorkerRegistered: false diff --git a/src/helper-ui.tsx b/src/helper-ui.tsx index 757f8000..e810e38b 100644 --- a/src/helper-ui.tsx +++ b/src/helper-ui.tsx @@ -1,8 +1,8 @@ import React, { useState, useEffect } from 'react' -import CidRenderer from './components/CidRenderer.tsx' -import Form from './components/Form.tsx' -import Header from './components/Header.tsx' -import { LOCAL_STORAGE_KEYS } from './lib/local-storage.ts' +import CidRenderer from './components/CidRenderer.jsx' +import Form from './components/Form.jsx' +import Header from './components/Header.jsx' +import { LOCAL_STORAGE_KEYS } from './lib/local-storage.js' export default function (): JSX.Element { const [requestPath, setRequestPath] = useState(localStorage.getItem(LOCAL_STORAGE_KEYS.forms.requestPath) ?? '') diff --git a/src/index.tsx b/src/index.tsx index d353d095..73b695b5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,11 +1,11 @@ import React from 'react' import ReactDOMClient from 'react-dom/client' import './app.css' -import App from './app.tsx' -import { ConfigProvider } from './context/config-context.tsx' -import { ServiceWorkerProvider } from './context/service-worker-context.tsx' -import { loadConfigFromLocalStorage } from './lib/config-db.ts' -import { isConfigPage } from './lib/is-config-page.ts' +import App from './app.jsx' +import { ConfigProvider } from './context/config-context.jsx' +import { ServiceWorkerProvider } from './context/service-worker-context.jsx' +import { loadConfigFromLocalStorage } from './lib/config-db.js' +import { isConfigPage } from './lib/is-config-page.js' await loadConfigFromLocalStorage() diff --git a/src/lib/channel.ts b/src/lib/channel.ts index 7d26ed56..cd9c0d39 100644 --- a/src/lib/channel.ts +++ b/src/lib/channel.ts @@ -1,5 +1,5 @@ -import { error } from './logger.ts' -import type { ChannelActions } from './common.ts' +import { error } from './logger.js' +import type { ChannelActions } from './common.js' export enum ChannelUsers { SW = 'SW', diff --git a/src/lib/config-db.ts b/src/lib/config-db.ts index 9c526fdd..0c2f4f77 100644 --- a/src/lib/config-db.ts +++ b/src/lib/config-db.ts @@ -1,5 +1,5 @@ import debugLib from 'debug' -import { LOCAL_STORAGE_KEYS } from './local-storage.ts' +import { LOCAL_STORAGE_KEYS } from './local-storage.js' import { log } from './logger' export interface ConfigDb { diff --git a/src/lib/deregister-request.ts b/src/lib/deregister-request.ts index fb8ed164..92528ab7 100644 --- a/src/lib/deregister-request.ts +++ b/src/lib/deregister-request.ts @@ -1,4 +1,4 @@ -import { trace } from './logger.ts' +import { trace } from './logger.js' // things are wonky with hash routes for deregistering. export function isDeregisterRequest (url: string): boolean { diff --git a/src/lib/get-subdomain-parts.ts b/src/lib/get-subdomain-parts.ts index 0977fbc3..5bb78db6 100644 --- a/src/lib/get-subdomain-parts.ts +++ b/src/lib/get-subdomain-parts.ts @@ -1,4 +1,4 @@ -import { dnsLinkLabelDecoder, isInlinedDnsLink } from './dns-link-labels.ts' +import { dnsLinkLabelDecoder, isInlinedDnsLink } from './dns-link-labels.js' export interface UrlParts { id: string | null diff --git a/src/lib/path-or-subdomain.ts b/src/lib/path-or-subdomain.ts index d729d657..c6737d96 100644 --- a/src/lib/path-or-subdomain.ts +++ b/src/lib/path-or-subdomain.ts @@ -1,7 +1,7 @@ import { base32 } from 'multiformats/bases/base32' import { base36 } from 'multiformats/bases/base36' import { CID } from 'multiformats/cid' -import { dnsLinkLabelEncoder } from './dns-link-labels.ts' +import { dnsLinkLabelEncoder } from './dns-link-labels.js' // TODO: dry, this is same regex code as in getSubdomainParts const subdomainRegex = /^(?[^/]+)\.(?ip[fn]s)\.[^/]+$/ diff --git a/src/redirectPage.tsx b/src/redirectPage.tsx index e669f299..d7281097 100644 --- a/src/redirectPage.tsx +++ b/src/redirectPage.tsx @@ -1,11 +1,11 @@ import React, { useContext, useEffect, useMemo, useState } from 'react' -import { ServiceWorkerReadyButton } from './components/sw-ready-button.tsx' -import { ServiceWorkerContext } from './context/service-worker-context.tsx' -import { HeliaServiceWorkerCommsChannel } from './lib/channel.ts' -import { setConfig, type ConfigDb } from './lib/config-db.ts' -import { getSubdomainParts } from './lib/get-subdomain-parts' +import { ServiceWorkerReadyButton } from './components/sw-ready-button.jsx' +import { ServiceWorkerContext } from './context/service-worker-context.jsx' +import { HeliaServiceWorkerCommsChannel } from './lib/channel.js' +import { setConfig, type ConfigDb } from './lib/config-db.js' +import { getSubdomainParts } from './lib/get-subdomain-parts.js' import { isConfigPage } from './lib/is-config-page' -import { error, trace } from './lib/logger.ts' +import { error, trace } from './lib/logger.js' const ConfigIframe = (): JSX.Element => { const { parentDomain } = getSubdomainParts(window.location.href) diff --git a/src/service-worker-utils.ts b/src/service-worker-utils.ts index d13b7b0b..4f1af857 100644 --- a/src/service-worker-utils.ts +++ b/src/service-worker-utils.ts @@ -1,4 +1,4 @@ -import { log } from './lib/logger.ts' +import { log } from './lib/logger.js' export async function registerServiceWorker (): Promise { const swRegistration = await navigator.serviceWorker.register(new URL(/* webpackChunkName: "sw" */'sw.ts', import.meta.url)) diff --git a/src/sw.ts b/src/sw.ts index 971daee4..d31bcff8 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -1,13 +1,13 @@ import { dnsJsonOverHttps } from '@helia/ipns/dns-resolvers' import { createVerifiedFetch, type VerifiedFetch } from '@helia/verified-fetch' -import { HeliaServiceWorkerCommsChannel, type ChannelMessage } from './lib/channel.ts' -import { getConfig } from './lib/config-db.ts' -import { contentTypeParser } from './lib/content-type-parser.ts' -import { getRedirectUrl, isDeregisterRequest } from './lib/deregister-request.ts' -import { getSubdomainParts } from './lib/get-subdomain-parts.ts' -import { isConfigPage } from './lib/is-config-page.ts' -import { error, log, trace } from './lib/logger.ts' -import { findOriginIsolationRedirect } from './lib/path-or-subdomain.ts' +import { HeliaServiceWorkerCommsChannel, type ChannelMessage } from './lib/channel.js' +import { getConfig } from './lib/config-db.js' +import { contentTypeParser } from './lib/content-type-parser.js' +import { getRedirectUrl, isDeregisterRequest } from './lib/deregister-request.js' +import { getSubdomainParts } from './lib/get-subdomain-parts.js' +import { isConfigPage } from './lib/is-config-page.js' +import { error, log, trace } from './lib/logger.js' +import { findOriginIsolationRedirect } from './lib/path-or-subdomain.js' /** ****************************************************** diff --git a/tsconfig.json b/tsconfig.json index d4dbe633..ce73d044 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,9 +13,9 @@ "moduleResolution": "bundler", "ignoreDeprecations": "5.0", // needed due to deprecated usage in tsconfig.aegir.json - "allowImportingTsExtensions": true, - "declaration": true, - "emitDeclarationOnly": true + // "allowImportingTsExtensions": true, + // "declaration": true, + // "emitDeclarationOnly": true }, "include": [ "src", From 2aad1805bc08b318de6c94e283a0227f421b487d Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:17:04 -0700 Subject: [PATCH 03/20] tmp: in progress build... --- .aegir.js | 35 +++++++++++++++++++++++++++++++++ patches/aegir+42.2.2.patch | 13 ++++++++++++ tests/dns-link-labels.spec.ts | 2 +- tests/path-or-subdomain.spec.ts | 2 +- tsconfig.json | 2 +- 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 patches/aegir+42.2.2.patch diff --git a/.aegir.js b/.aegir.js index 3200815e..ba300c8a 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,5 +1,40 @@ +import { readFile } from "fs/promises" + +/** + * @type {import ('esbuild').Plugin} + */ +export const CSSMinifyPlugin = { + name: "CSSMinifyPlugin", + setup(build) { + build.onLoad({ filter: /\.css$/ }, async (args) => { + const f = await readFile(args.path) + const css = await transform(f, { loader: "css", minify: true }) + return { loader: "text", contents: css.code } + }) + } +} + /** @type {import('aegir').PartialOptions} */ export default { + build: { + config: { + // TSC is done first, so inputs to esbuild is dist/src/* + entryPoints: ['dist/src/index.jsx', 'dist/src/sw.js'], + // outfile: null, + outdir: 'dist', + // minify: true, + chunkNames: 'ipfs-sw-[name]', + splitting: true, + loader: { + '.svg': 'dataurl' + }, + format: 'esm', + plugins: [CSSMinifyPlugin] + } + }, + test: { + files: ['test/node.ts'] + }, lint: { files: ['src/**/*.ts', 'src/**/*.tsx', 'tests/**/*'] }, diff --git a/patches/aegir+42.2.2.patch b/patches/aegir+42.2.2.patch new file mode 100644 index 00000000..042842b3 --- /dev/null +++ b/patches/aegir+42.2.2.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/aegir/src/build/index.js b/node_modules/aegir/src/build/index.js +index 54537df..52e15cc 100644 +--- a/node_modules/aegir/src/build/index.js ++++ b/node_modules/aegir/src/build/index.js +@@ -48,7 +48,7 @@ const build = async (argv) => { + banner: { js: umdPre }, + footer: { js: umdPost }, + metafile: argv.bundlesize, +- outfile, ++ outfile: argv.fileConfig.build.config.outfile == null ? undefined : outfile, + define: { + global: 'globalThis', + 'process.env.NODE_ENV': '"production"' diff --git a/tests/dns-link-labels.spec.ts b/tests/dns-link-labels.spec.ts index 1fafbb69..3c8416ea 100644 --- a/tests/dns-link-labels.spec.ts +++ b/tests/dns-link-labels.spec.ts @@ -1,6 +1,6 @@ /* eslint-env mocha */ import { expect } from 'aegir/chai' -import { dnsLinkLabelDecoder, dnsLinkLabelEncoder } from '../src/lib/dns-link-labels.ts' +import { dnsLinkLabelDecoder, dnsLinkLabelEncoder } from '../src/lib/dns-link-labels.js' describe('dns-link-labels', () => { it('should support specs-ipfs-tech', () => { diff --git a/tests/path-or-subdomain.spec.ts b/tests/path-or-subdomain.spec.ts index a940bbe0..babf9a05 100644 --- a/tests/path-or-subdomain.spec.ts +++ b/tests/path-or-subdomain.spec.ts @@ -1,6 +1,6 @@ /* eslint-env mocha */ import { expect } from 'aegir/chai' -import { isPathOrSubdomainRequest } from '../src/lib/path-or-subdomain.ts' +import { isPathOrSubdomainRequest } from '../src/lib/path-or-subdomain.js' describe('isPathOrSubdomainRequest', () => { it('returns true for path-based request', () => { diff --git a/tsconfig.json b/tsconfig.json index ce73d044..4e107a7f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "noImplicitAny": false, // todo: update "module": "es2022", "target": "es2022", - "jsx": "react", + "jsx": "preserve", "lib": ["WebWorker", "ES6", "DOM"], "strictNullChecks": true, From 88b4b8bd0af3f6016f4c2d4f373e81a1f5125570 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:31:56 -0700 Subject: [PATCH 04/20] test-esbuild.js script works.. need to fix names --- package-lock.json | 129 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + test-esbuild.js | 59 +++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100755 test-esbuild.js diff --git a/package-lock.json b/package-lock.json index f8f01766..75e64565 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "babel-loader": "^9.1.3", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.10.0", + "esbuild-plugin-copy": "^2.1.1", "eslint-config-standard-with-typescript": "^34.0.1", "html-webpack-plugin": "^5.5.0", "npm-run-all": "^4.1.5", @@ -9516,6 +9517,134 @@ "@esbuild/win32-x64": "0.19.12" } }, + "node_modules/esbuild-plugin-copy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/esbuild-plugin-copy/-/esbuild-plugin-copy-2.1.1.tgz", + "integrity": "sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "fs-extra": "^10.0.1", + "globby": "^11.0.3" + }, + "peerDependencies": { + "esbuild": ">= 0.14.0" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/esbuild-plugin-copy/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esbuild-plugin-copy/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/esbuild-plugin-wasm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/esbuild-plugin-wasm/-/esbuild-plugin-wasm-1.1.0.tgz", diff --git a/package.json b/package.json index 2e8a3bc9..24458c9e 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "babel-loader": "^9.1.3", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.10.0", + "esbuild-plugin-copy": "^2.1.1", "eslint-config-standard-with-typescript": "^34.0.1", "html-webpack-plugin": "^5.5.0", "npm-run-all": "^4.1.5", diff --git a/test-esbuild.js b/test-esbuild.js new file mode 100755 index 00000000..853f5010 --- /dev/null +++ b/test-esbuild.js @@ -0,0 +1,59 @@ +import esbuild from 'esbuild' +import { copy } from 'esbuild-plugin-copy' + +// Define paths +const paths = { + src: 'src', + public: 'public', + dist: 'dist' +} + +// Copy plugin configuration +const copyPlugin = copy({ + assets: { + from: [`${paths.public}/_redirects`, `${paths.public}/favicon.ico`, `${paths.public}/index.html`, `${paths.public}/**/*.svg`, `${paths.public}/**/*.css`], + to: ['.'] + } +}) + +// Main build function +const build = async () => { + // Build the main application + await esbuild.build({ + entryPoints: [`${paths.src}/index.tsx`], + bundle: true, + splitting: true, + format: 'esm', + outdir: paths.dist, + loader: { + '.svg': 'dataurl' + }, + chunkNames: 'ipfs-sw-[name]', + minify: true, + sourcemap: true, + target: 'esnext', + plugins: [copyPlugin], + define: { 'process.env.NODE_ENV': '"production"' } + }) + + // Build the service worker + await esbuild.build({ + entryPoints: [`${paths.src}/sw.ts`], + bundle: true, + outdir: paths.dist, + chunkNames: 'ipfs-sw-[name]', + splitting: false, + format: 'esm', + minify: true, + sourcemap: true, + target: 'esnext', + define: { 'process.env.NODE_ENV': '"production"' } + }) + + // Additional configurations like React vendor chunk splitting + // might require custom logic or third-party tools as esbuild's + // chunk splitting capabilities are not as extensive as Webpack's. +} + +// eslint-disable-next-line no-console +build().catch((e) => console.error(e)) From c13a10eb48bbb64399b68d81237dba5ab1055e50 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:18:22 -0700 Subject: [PATCH 05/20] chore: making progress --- .aegir.js | 35 +++++-- package-lock.json | 227 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 7 +- test-esbuild.js | 2 +- tsconfig.json | 2 +- webpack.config.js | 104 ++++++++++----------- 6 files changed, 304 insertions(+), 73 deletions(-) diff --git a/.aegir.js b/.aegir.js index 41e3600b..2101bcda 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,4 +1,12 @@ -import { readFile } from "fs/promises" +import { readFile } from 'fs/promises' +import { copy } from 'esbuild-plugin-copy' + +// Define paths +const paths = { + src: 'dist-tsc', + public: 'public', + dist: 'dist-esbuild' +} /** * @type {import ('esbuild').Plugin} @@ -13,23 +21,32 @@ export const CSSMinifyPlugin = { }) } } +// Copy plugin configuration +const copyPlugin = copy({ + assets: { + from: [`${paths.public}/_redirects`, `${paths.public}/favicon.ico`, `${paths.public}/index.html`, `${paths.public}/**/*.svg`, `${paths.public}/**/*.css`], + to: ['./public'] + } +}) /** @type {import('aegir').PartialOptions} */ export default { build: { config: { - // TSC is done first, so inputs to esbuild is dist/src/* - entryPoints: ['dist/src/index.jsx', 'dist/src/sw.js'], - // outfile: null, - outdir: 'dist', - // minify: true, - chunkNames: 'ipfs-sw-[name]', + entryPoints: [`${paths.src}/src/index.jsx`, `${paths.src}/src/sw.js`], + bundle: true, splitting: true, + format: 'esm', + outdir: paths.dist, loader: { '.svg': 'dataurl' }, - format: 'esm', - plugins: [CSSMinifyPlugin] + chunkNames: 'ipfs-sw-[name]', + minify: true, + sourcemap: true, + target: 'esnext', + plugins: [copyPlugin], + define: { 'process.env.NODE_ENV': '"production"' } } }, test: { diff --git a/package-lock.json b/package-lock.json index 7f96ccce..1fd40825 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "aegir": "^42.2.2", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^11.0.0", + "copyfiles": "^2.4.1", "css-loader": "^6.10.0", "esbuild-plugin-copy": "^2.1.1", "eslint-config-standard-with-typescript": "^34.0.1", @@ -7744,6 +7745,158 @@ "webpack": "^5.1.0" } }, + "node_modules/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "dev": true, + "dependencies": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "copyfiles": "copyfiles", + "copyup": "copyfiles" + } + }, + "node_modules/copyfiles/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/copyfiles/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/copyfiles/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/copyfiles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/copyfiles/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/copyfiles/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/copyfiles/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/copyfiles/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/copyfiles/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/copyfiles/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/copyfiles/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/core-js-compat": { "version": "3.29.1", "dev": true, @@ -11510,15 +11663,6 @@ "through2": "~2.0.0" } }, - "node_modules/git-log-parser/node_modules/through2": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, "node_modules/glob": { "version": "7.2.0", "dev": true, @@ -15434,6 +15578,18 @@ "node": ">=8" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha": { "version": "10.2.0", "dev": true, @@ -15969,6 +16125,40 @@ "dev": true, "license": "MIT" }, + "node_modules/noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, + "node_modules/noms/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/noms/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/noms/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, "node_modules/normalize-package-data": { "version": "3.0.3", "dev": true, @@ -24273,6 +24463,16 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -25249,6 +25449,15 @@ "node": ">= 0.8" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.10", "dev": true, diff --git a/package.json b/package.json index 6c9c553c..6cfe2e03 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,14 @@ "license": "MIT", "scripts": { "analyze-bundle": "webpack --env analyze", - "clean": "aegir clean", + "clean": "aegir clean dist dist-tsc dist-esbuild", "dep-check": "aegir dep-check", "lint": "aegir lint", "lint:fix": "aegir lint --fix", - "build": "run-s dep-check lint build:webpack", + "build": "run-s dep-check lint copy:non-tsc build:aegir build:webpack", + "copy:non-tsc": "npx copyfiles src/*.svg src/**/*.svg src/*.css src/**/*.css dist-tsc", "postbuild": "aegir test --build=false", + "build:aegir": "aegir build", "build:webpack": "webpack --env production", "serve": "webpack serve --mode=development", "serve:prod": "webpack serve --mode=production", @@ -52,6 +54,7 @@ "aegir": "^42.2.2", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^11.0.0", + "copyfiles": "^2.4.1", "css-loader": "^6.10.0", "esbuild-plugin-copy": "^2.1.1", "eslint-config-standard-with-typescript": "^34.0.1", diff --git a/test-esbuild.js b/test-esbuild.js index 853f5010..4ca93e16 100755 --- a/test-esbuild.js +++ b/test-esbuild.js @@ -5,7 +5,7 @@ import { copy } from 'esbuild-plugin-copy' const paths = { src: 'src', public: 'public', - dist: 'dist' + dist: 'dist-esbuild' } // Copy plugin configuration diff --git a/tsconfig.json b/tsconfig.json index 85f0346a..abc1cdb2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "./node_modules/aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist", + "outDir": "dist-tsc", "allowJs": true, "noImplicitAny": false, // todo: update "module": "es2022", diff --git a/webpack.config.js b/webpack.config.js index d4e9f52b..c27b925c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -52,16 +52,17 @@ const splitChunks = { const paths = { // Source files - src: path.resolve(__dirname, './src'), - testSrc: path.resolve(__dirname, './webpack-tests'), + src: path.resolve(__dirname, './dist-esbuild'), + // testSrc: path.resolve(__dirname, './webpack-tests'), testBuild: path.resolve(__dirname, './test-build'), // Production build files - build: path.resolve(__dirname, './dist'), + build: path.resolve(__dirname, './dist') - // Static files that get copied to build folder - public: path.resolve(__dirname, './public') + // public: path.resolve(__dirname, './public') } +// Static files that get copied to build folder +paths.public = path.resolve(paths.src, './public') /** * @type {import('webpack').Configuration} @@ -153,24 +154,24 @@ const dev = { } } -/** - * @type {import('webpack').Configuration} - */ -const test = { - mode: 'development', - devtool: 'inline-source-map', - output: { - path: paths.testBuild, - filename: 'tests.js' - }, - entry: { - tests: readdirSync(paths.testSrc).filter(function (file) { - return file.match(/.*\.ts$/) - }).map(function (file) { - return path.join(paths.testSrc, file) - }) - } -} +// /** +// * @type {import('webpack').Configuration} +// */ +// const test = { +// mode: 'development', +// devtool: 'inline-source-map', +// output: { +// path: paths.testBuild, +// filename: 'tests.js' +// }, +// entry: { +// tests: readdirSync(paths.testSrc).filter(function (file) { +// return file.match(/.*\.ts$/) +// }).map(function (file) { +// return path.join(paths.testSrc, file) +// }) +// } +// } /** * @type {import('webpack').Configuration} @@ -178,7 +179,7 @@ const test = { const common = { // Where webpack looks to start building the bundle entry: { - main: paths.src + '/index.tsx' + main: paths.src + '/index.js' }, output: { path: paths.build, @@ -223,27 +224,27 @@ const common = { module: { rules: [ // JavaScript: Use Babel to transpile JavaScript files - { - test: /\.[jt]sx?$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader', - options: { - presets: [ - '@babel/preset-typescript', - [ - '@babel/preset-env', - { - targets: { - esmodules: true - } - } - ], - '@babel/preset-react' - ] - } - } - }, + // { + // test: /\.[j]s?$/, + // exclude: /node_modules/, + // use: { + // loader: 'babel-loader', + // options: { + // presets: [ + // '@babel/preset-typescript', + // [ + // '@babel/preset-env', + // { + // targets: { + // esmodules: true + // } + // } + // ], + // '@babel/preset-react' + // ] + // } + // } + // }, // Images: Copy image files to build folder { test: /\.(?:ico|gif|png|jpg|jpeg)$/i, type: 'asset/resource' }, @@ -281,12 +282,13 @@ const common = { export default (cmd) => { const production = cmd.production let config = prod - if (cmd.test) { - config = test - const testConfig = merge(common, test) - testConfig.entry = test.entry - return testConfig - } else if (cmd.analyze) { + // if (cmd.test) { + // config = test + // const testConfig = merge(common, test) + // testConfig.entry = test.entry + // return testConfig + // } else + if (cmd.analyze) { config = prod prod.plugins.push( new BundleAnalyzerPlugin.BundleAnalyzerPlugin({ From 7e9e09076fd0f7d539578db8068ea5e04b823e25 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:56:29 -0700 Subject: [PATCH 06/20] feat: build works --- .aegir.js | 52 +++++++++++++------ .gitignore | 2 + package.json | 7 +-- patches/aegir+42.2.2.patch | 37 +++++++++++-- src/service-worker-utils.ts | 2 +- test-esbuild.js | 6 +-- .../dns-link-labels.spec.ts | 0 test/{node.js => node.ts} | 2 +- .../path-or-subdomain.spec.ts | 0 tsconfig.json | 6 +-- webpack.config.js | 28 +--------- 11 files changed, 85 insertions(+), 57 deletions(-) rename {webpack-tests => test}/dns-link-labels.spec.ts (100%) rename test/{node.js => node.ts} (96%) rename {webpack-tests => test}/path-or-subdomain.spec.ts (100%) diff --git a/.aegir.js b/.aegir.js index 2101bcda..f44ccc39 100644 --- a/.aegir.js +++ b/.aegir.js @@ -21,39 +21,59 @@ export const CSSMinifyPlugin = { }) } } + // Copy plugin configuration const copyPlugin = copy({ - assets: { - from: [`${paths.public}/_redirects`, `${paths.public}/favicon.ico`, `${paths.public}/index.html`, `${paths.public}/**/*.svg`, `${paths.public}/**/*.css`], - to: ['./public'] - } + verbose: true, + assets: [ + { + from: [ + `${paths.public}/_redirects`, + `${paths.public}/favicon.ico`, + `${paths.public}/index.html`, + ], + to: ['./public'] + }, + { + from: [ + `${paths.src}/**/*.svg`, + `${paths.src}/**/*.css` + ], + to: ['.'] + } + ] }) /** @type {import('aegir').PartialOptions} */ export default { build: { config: { + banner: {}, + footer: {}, entryPoints: [`${paths.src}/src/index.jsx`, `${paths.src}/src/sw.js`], - bundle: true, - splitting: true, + // bundle: true, + // splitting: true, format: 'esm', outdir: paths.dist, loader: { '.svg': 'dataurl' }, - chunkNames: 'ipfs-sw-[name]', - minify: true, + // chunkNames: 'ipfs-sw-[name]', + // minify: true, sourcemap: true, - target: 'esnext', + // target: 'esnext', plugins: [copyPlugin], define: { 'process.env.NODE_ENV': '"production"' } } }, - test: { - files: ['test/node.ts'] - }, + test: { }, lint: { - files: ['src/**/*.ts', 'src/**/*.tsx', 'tests/**/*', 'test/**/*.js'] + files: [ + 'src/**/*.[jt]s', + 'src/**/*.[jt]sx', + 'test/**/*.[jt]s', + 'test/**/*.[jt]sx' + ] }, dependencyCheck: { ignore: [ @@ -67,12 +87,14 @@ export default { 'webpack-dev-server', 'babel-loader', 'style-loader', - 'css-loader' + 'css-loader', + 'esbuild' ], productionIgnorePatterns: [ 'webpack.config.js', '.aegir.js', - '/tests', + '/test', + 'test-esbuild.js', 'dist' ] } diff --git a/.gitignore b/.gitignore index e45263bd..22d41ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -217,3 +217,5 @@ $RECYCLE.BIN/ # End of https://www.toptal.com/developers/gitignore/api/macos,node,windows,linux .vscode +dist-tsc +dist-esbuild diff --git a/package.json b/package.json index 6cfe2e03..658de3e7 100644 --- a/package.json +++ b/package.json @@ -12,16 +12,17 @@ "dep-check": "aegir dep-check", "lint": "aegir lint", "lint:fix": "aegir lint --fix", - "build": "run-s dep-check lint copy:non-tsc build:aegir build:webpack", + "build": "run-s build:esm build:webpack", "copy:non-tsc": "npx copyfiles src/*.svg src/**/*.svg src/*.css src/**/*.css dist-tsc", "postbuild": "aegir test --build=false", + "build:esm": "run-s copy:non-tsc build:aegir", "build:aegir": "aegir build", "build:webpack": "webpack --env production", "serve": "webpack serve --mode=development", "serve:prod": "webpack serve --mode=production", "start": "npm run serve", - "test": "npm run test:node", - "test:node": "webpack --env test && npx mocha test-build/tests.js", + "test": "aegir test -f dist-tsc/test/**/*.spec.js", + "test:node": "aegir test -t node -f dist-tsc/test/node.js", "postinstall": "patch-package" }, "browser": "./dist/src/index.js", diff --git a/patches/aegir+42.2.2.patch b/patches/aegir+42.2.2.patch index 042842b3..92e4b505 100644 --- a/patches/aegir+42.2.2.patch +++ b/patches/aegir+42.2.2.patch @@ -1,13 +1,42 @@ diff --git a/node_modules/aegir/src/build/index.js b/node_modules/aegir/src/build/index.js -index 54537df..52e15cc 100644 +index 54537df..6df4c61 100644 --- a/node_modules/aegir/src/build/index.js +++ b/node_modules/aegir/src/build/index.js -@@ -48,7 +48,7 @@ const build = async (argv) => { - banner: { js: umdPre }, - footer: { js: umdPost }, +@@ -36,26 +36,31 @@ const build = async (argv) => { + entryPoint = fromRoot('dist', 'src', 'index.js') + } + +- const result = await esbuild.build(defaults( ++ const config = defaults( + { + entryPoints: [entryPoint], + bundle: true, +- format: 'iife', ++ format: argv.fileConfig.build.config.format ?? 'iife', + conditions: ['production'], + sourcemap: argv.bundlesize, + minify: true, + globalName, +- banner: { js: umdPre }, +- footer: { js: umdPost }, ++ banner: argv.fileConfig.build.config.banner ?? { js: umdPre }, ++ footer: argv.fileConfig.build.config.footer ?? { js: umdPost }, metafile: argv.bundlesize, - outfile, + outfile: argv.fileConfig.build.config.outfile == null ? undefined : outfile, define: { global: 'globalThis', 'process.env.NODE_ENV': '"production"' + } + }, + argv.fileConfig.build.config +- )) ++ ) ++ if (config.outdir) { ++ delete config.outfile ++ } ++ ++ const result = await esbuild.build(config) + + if (result.metafile) { + fs.writeJSONSync(path.join(paths.dist, 'stats.json'), result.metafile) diff --git a/src/service-worker-utils.ts b/src/service-worker-utils.ts index 4f1af857..0bf650a2 100644 --- a/src/service-worker-utils.ts +++ b/src/service-worker-utils.ts @@ -1,7 +1,7 @@ import { log } from './lib/logger.js' export async function registerServiceWorker (): Promise { - const swRegistration = await navigator.serviceWorker.register(new URL(/* webpackChunkName: "sw" */'sw.ts', import.meta.url)) + const swRegistration = await navigator.serviceWorker.register(new URL(/* webpackChunkName: "sw" */'sw.js', import.meta.url)) return new Promise((resolve, reject) => { swRegistration.addEventListener('updatefound', () => { const newWorker = swRegistration.installing diff --git a/test-esbuild.js b/test-esbuild.js index 4ca93e16..5029ed49 100755 --- a/test-esbuild.js +++ b/test-esbuild.js @@ -5,7 +5,7 @@ import { copy } from 'esbuild-plugin-copy' const paths = { src: 'src', public: 'public', - dist: 'dist-esbuild' + dist: 'dist-esbuild2' } // Copy plugin configuration @@ -21,8 +21,8 @@ const build = async () => { // Build the main application await esbuild.build({ entryPoints: [`${paths.src}/index.tsx`], - bundle: true, - splitting: true, + // bundle: true, + // splitting: true, format: 'esm', outdir: paths.dist, loader: { diff --git a/webpack-tests/dns-link-labels.spec.ts b/test/dns-link-labels.spec.ts similarity index 100% rename from webpack-tests/dns-link-labels.spec.ts rename to test/dns-link-labels.spec.ts diff --git a/test/node.js b/test/node.ts similarity index 96% rename from test/node.js rename to test/node.ts index 7e082541..fa4df2a0 100755 --- a/test/node.js +++ b/test/node.ts @@ -7,7 +7,7 @@ import { expect } from 'aegir/chai' const cwd = dirname(fileURLToPath(import.meta.url)) -describe('verify-dist', async () => { +describe.skip('verify-dist', () => { it('has a service worker with the correct name', async () => { /** * test to confirm that the service worker generated in /dist does not change names diff --git a/webpack-tests/path-or-subdomain.spec.ts b/test/path-or-subdomain.spec.ts similarity index 100% rename from webpack-tests/path-or-subdomain.spec.ts rename to test/path-or-subdomain.spec.ts diff --git a/tsconfig.json b/tsconfig.json index abc1cdb2..6c86651c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,14 +12,14 @@ "moduleResolution": "bundler", - "ignoreDeprecations": "5.0", // needed due to deprecated usage in tsconfig.aegir.json + "ignoreDeprecations": "5.0" // needed due to deprecated usage in tsconfig.aegir.json // "allowImportingTsExtensions": true, // "declaration": true, // "emitDeclarationOnly": true }, "include": [ "src", - "webpack-tests", - "types/svg.d.ts" + "types/svg.d.ts", + "test" ] } diff --git a/webpack.config.js b/webpack.config.js index c27b925c..89a88eb2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,3 @@ -import { readdirSync } from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import CopyWebpackPlugin from 'copy-webpack-plugin' @@ -30,7 +29,7 @@ const splitChunks = { enforce: true }, sw: { - test: /[\\/]src[\\/]sw.ts/, + test: /[\\/]src[\\/]sw.js/, name: 'sw', priority: 100, // anything the sw needs should be in the sw chunk chunks: 'all' @@ -154,25 +153,6 @@ const dev = { } } -// /** -// * @type {import('webpack').Configuration} -// */ -// const test = { -// mode: 'development', -// devtool: 'inline-source-map', -// output: { -// path: paths.testBuild, -// filename: 'tests.js' -// }, -// entry: { -// tests: readdirSync(paths.testSrc).filter(function (file) { -// return file.match(/.*\.ts$/) -// }).map(function (file) { -// return path.join(paths.testSrc, file) -// }) -// } -// } - /** * @type {import('webpack').Configuration} */ @@ -282,12 +262,6 @@ const common = { export default (cmd) => { const production = cmd.production let config = prod - // if (cmd.test) { - // config = test - // const testConfig = merge(common, test) - // testConfig.entry = test.entry - // return testConfig - // } else if (cmd.analyze) { config = prod prod.plugins.push( From 9e0214ffa67cb499ee6bf5efbb32075cb32bea88 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:20:03 -0700 Subject: [PATCH 07/20] chore: some cleanup --- .aegir.js | 5 +++-- package.json | 4 ++-- test/node.ts | 49 +++++++++++++++++++++++++++++++++++------------ webpack.config.js | 29 ++++------------------------ 4 files changed, 46 insertions(+), 41 deletions(-) diff --git a/.aegir.js b/.aegir.js index f44ccc39..e2d43e7b 100644 --- a/.aegir.js +++ b/.aegir.js @@ -24,7 +24,6 @@ export const CSSMinifyPlugin = { // Copy plugin configuration const copyPlugin = copy({ - verbose: true, assets: [ { from: [ @@ -51,10 +50,12 @@ export default { banner: {}, footer: {}, entryPoints: [`${paths.src}/src/index.jsx`, `${paths.src}/src/sw.js`], + minify: false, // bundle: true, - // splitting: true, + splitting: true, format: 'esm', outdir: paths.dist, + chunkNames: 'ipfs-sw-[name]', loader: { '.svg': 'dataurl' }, diff --git a/package.json b/package.json index 658de3e7..bba6c1d8 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,11 @@ "lint:fix": "aegir lint --fix", "build": "run-s build:esm build:webpack", "copy:non-tsc": "npx copyfiles src/*.svg src/**/*.svg src/*.css src/**/*.css dist-tsc", - "postbuild": "aegir test --build=false", "build:esm": "run-s copy:non-tsc build:aegir", "build:aegir": "aegir build", "build:webpack": "webpack --env production", - "serve": "webpack serve --mode=development", + "serve": "run-s build:esm serve:dev", + "serve:dev": "webpack serve --mode=development", "serve:prod": "webpack serve --mode=production", "start": "npm run serve", "test": "aegir test -f dist-tsc/test/**/*.spec.js", diff --git a/test/node.ts b/test/node.ts index fa4df2a0..aff9f8cc 100755 --- a/test/node.ts +++ b/test/node.ts @@ -7,18 +7,43 @@ import { expect } from 'aegir/chai' const cwd = dirname(fileURLToPath(import.meta.url)) -describe.skip('verify-dist', () => { - it('has a service worker with the correct name', async () => { - /** - * test to confirm that the service worker generated in /dist does not change names - */ - await expect(access(resolve(cwd, '../dist/ipfs-sw-sw.js'), constants.F_OK)).to.not.be.rejected() - }) +const distRoot = resolve(cwd, '../../dist') + +describe(`verify-dist at ${distRoot}`, () => { + const distTests = [ + { + file: 'ipfs-sw-sw.js' + }, + { + file: 'ipfs-sw-styles.css', + content: ['47vh', '.local-storage-toggle'] + } + ] - it('has css file with expected content', async () => { - await expect(access(resolve(cwd, '../dist/ipfs-sw-styles.css'), constants.F_OK)).to.not.be.rejected() - const contents = await readFile(resolve(cwd, '../dist/ipfs-sw-styles.css'), 'utf8') - expect(contents).to.include('47vh') - expect(contents).to.include('.local-storage-toggle input.status') + distTests.forEach(({ file, content }) => { + it(`has ${file} with expected content`, async () => { + const filePath = resolve(distRoot, file) + await expect(access(filePath, constants.F_OK)).to.not.be.rejected() + if (content != null) { + const fileContents = await readFile(filePath, 'utf8') + content.forEach((c) => { + expect(fileContents).to.include(c) + }) + } + }) }) + + // it('has a service worker with the correct name', async () => { + // /** + // * test to confirm that the service worker generated in /dist does not change names + // */ + // await expect(access(resolve(distRoot, 'ipfs-sw-sw.js'), constants.F_OK)).to.not.be.rejected() + // }) + + // it('has css file with expected content', async () => { + // await expect(access(resolve(distRoot, 'ipfs-sw-styles.css'), constants.F_OK)).to.not.be.rejected() + // const contents = await readFile(resolve(distRoot, 'ipfs-sw-styles.css'), 'utf8') + // expect(contents).to.include('47vh') + // expect(contents).to.include('.local-storage-toggle input.status') + // }) }) diff --git a/webpack.config.js b/webpack.config.js index 89a88eb2..76c1eb04 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,7 +17,7 @@ const splitChunks = { cacheGroups: { defaultVendors: { // everything not specified in other cache groups name: 'vendor-rest', - test: /[\\/]node_modules[\\/]/, + // test: /[\\/]node_modules[\\/]/, priority: -10, chunks: 'all' }, @@ -57,8 +57,6 @@ const paths = { // Production build files build: path.resolve(__dirname, './dist') - - // public: path.resolve(__dirname, './public') } // Static files that get copied to build folder paths.public = path.resolve(paths.src, './public') @@ -181,6 +179,7 @@ const common = { // Generates an HTML file from a template // Generates deprecation warning: https://github.com/jantimon/html-webpack-plugin/issues/1501 + // TODO: replace with something like https://github.com/craftamap/esbuild-plugin-html ? new HtmlWebpackPlugin({ excludeChunks: ['sw'], title: 'IPFS Service Worker Gateway', @@ -203,28 +202,8 @@ const common = { // Determine how modules within the project are treated module: { rules: [ - // JavaScript: Use Babel to transpile JavaScript files - // { - // test: /\.[j]s?$/, - // exclude: /node_modules/, - // use: { - // loader: 'babel-loader', - // options: { - // presets: [ - // '@babel/preset-typescript', - // [ - // '@babel/preset-env', - // { - // targets: { - // esmodules: true - // } - // } - // ], - // '@babel/preset-react' - // ] - // } - // } - // }, + // aegir has already built the JS for us with tsc & esbuild + // { test: /\.[j]s?$/,}, // Images: Copy image files to build folder { test: /\.(?:ico|gif|png|jpg|jpeg)$/i, type: 'asset/resource' }, From 2264a648e46c59b42be67f1960ea59ca8433aff9 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:38:08 -0700 Subject: [PATCH 08/20] fix: generate styles --- webpack.config.js | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 76c1eb04..5d519386 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,14 +17,14 @@ const splitChunks = { cacheGroups: { defaultVendors: { // everything not specified in other cache groups name: 'vendor-rest', - // test: /[\\/]node_modules[\\/]/, + test: /[\\/]node_modules[\\/]/, priority: -10, chunks: 'all' }, styles: { minChunks: 1, name: 'styles', - type: 'css/mini-extract', + test: /.+\.css/, chunks: 'initial', enforce: true }, @@ -51,15 +51,16 @@ const splitChunks = { const paths = { // Source files - src: path.resolve(__dirname, './dist-esbuild'), + distTsc: path.resolve(__dirname, './dist-tsc/src'), // testSrc: path.resolve(__dirname, './webpack-tests'), testBuild: path.resolve(__dirname, './test-build'), // Production build files - build: path.resolve(__dirname, './dist') + build: path.resolve(__dirname, './dist'), + + // Static files that get copied to build folder + public: path.resolve(__dirname, './public') } -// Static files that get copied to build folder -paths.public = path.resolve(paths.src, './public') /** * @type {import('webpack').Configuration} @@ -157,7 +158,7 @@ const dev = { const common = { // Where webpack looks to start building the bundle entry: { - main: paths.src + '/index.js' + main: paths.distTsc + '/index.jsx' }, output: { path: paths.build, @@ -204,6 +205,27 @@ const common = { rules: [ // aegir has already built the JS for us with tsc & esbuild // { test: /\.[j]s?$/,}, + { + test: /\.jsx$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-typescript', + [ + '@babel/preset-env', + { + targets: { + esmodules: true + } + } + ], + '@babel/preset-react' + ] + } + } + }, // Images: Copy image files to build folder { test: /\.(?:ico|gif|png|jpg|jpeg)$/i, type: 'asset/resource' }, @@ -221,7 +243,7 @@ const common = { }, resolve: { - modules: [paths.src, 'node_modules'], + modules: [paths.distTsc, 'node_modules'], extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] }, From c269e2d205914bf82a4ea6f3100dd226d4d3cf41 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:41:29 -0700 Subject: [PATCH 09/20] chore: cleanup build scripts --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index bba6c1d8..519e80bd 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,8 @@ "lint": "aegir lint", "lint:fix": "aegir lint --fix", "build": "run-s build:esm build:webpack", - "copy:non-tsc": "npx copyfiles src/*.svg src/**/*.svg src/*.css src/**/*.css dist-tsc", - "build:esm": "run-s copy:non-tsc build:aegir", - "build:aegir": "aegir build", + "prebuild:esm": "copyfiles src/*.svg src/**/*.svg src/*.css src/**/*.css dist-tsc", + "build:esm": "tsc", "build:webpack": "webpack --env production", "serve": "run-s build:esm serve:dev", "serve:dev": "webpack serve --mode=development", From 45bed88ba10f84163513ffdd2b148905da06bea6 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:51:47 -0700 Subject: [PATCH 10/20] fix: npm run start --- package.json | 3 +-- webpack.config.js | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 519e80bd..a0b5f331 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,7 @@ "prebuild:esm": "copyfiles src/*.svg src/**/*.svg src/*.css src/**/*.css dist-tsc", "build:esm": "tsc", "build:webpack": "webpack --env production", - "serve": "run-s build:esm serve:dev", - "serve:dev": "webpack serve --mode=development", + "serve": "webpack serve --mode=development", "serve:prod": "webpack serve --mode=production", "start": "npm run serve", "test": "aegir test -f dist-tsc/test/**/*.spec.js", diff --git a/webpack.config.js b/webpack.config.js index 5d519386..3f06bc92 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -52,6 +52,7 @@ const splitChunks = { const paths = { // Source files distTsc: path.resolve(__dirname, './dist-tsc/src'), + devSrc: path.resolve(__dirname, './src'), // testSrc: path.resolve(__dirname, './webpack-tests'), testBuild: path.resolve(__dirname, './test-build'), @@ -123,6 +124,9 @@ const prod = { const dev = { // Set the mode to development or production mode: 'development', + entry: { + main: paths.devSrc + '/index.tsx' + }, // Control how source maps are generated devtool: 'inline-source-map', @@ -206,7 +210,8 @@ const common = { // aegir has already built the JS for us with tsc & esbuild // { test: /\.[j]s?$/,}, { - test: /\.jsx$/, + // test: /\.jsx$/, + test: /\.[jt]sx?$/, exclude: /node_modules/, use: { loader: 'babel-loader', @@ -244,7 +249,13 @@ const common = { resolve: { modules: [paths.distTsc, 'node_modules'], - extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] + extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], + extensionAlias: { + '.js': ['.js', '.ts'], + '.jsx': ['.jsx', '.tsx'], + '.cjs': ['.cjs', '.cts'], + '.mjs': ['.mjs', '.mts'] + } }, // fix: https://github.com/webpack/webpack-dev-server/issues/2758 From a80061b649310b484416b8c902ab6a320e9f3f7a Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:46:50 -0700 Subject: [PATCH 11/20] feat: playwright testing support --- .gitignore | 4 ++ package-lock.json | 61 +++++++++++++++++++++++ package.json | 9 +++- playwright.config.js | 47 ++++++++++++++++++ src/components/Header.tsx | 6 +-- src/components/config.tsx | 12 ++--- src/components/local-storage-input.tsx | 8 ++-- src/components/local-storage-toggle.tsx | 8 ++-- test-e2e/global-setup.js | 64 +++++++++++++++++++++++++ test-e2e/layout.test.ts | 49 +++++++++++++++++++ tsconfig.json | 4 +- 11 files changed, 251 insertions(+), 21 deletions(-) create mode 100644 playwright.config.js create mode 100644 test-e2e/global-setup.js create mode 100644 test-e2e/layout.test.ts diff --git a/.gitignore b/.gitignore index 22d41ed8..211d2628 100644 --- a/.gitignore +++ b/.gitignore @@ -219,3 +219,7 @@ $RECYCLE.BIN/ .vscode dist-tsc dist-esbuild +playwright-report +.envrc +.direnv +*.nix diff --git a/package-lock.json b/package-lock.json index 1fd40825..f2efdc03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", + "@playwright/test": "^1.42.1", "@types/react": "^18.0.31", "aegir": "^42.2.2", "babel-loader": "^9.1.3", @@ -36,6 +37,7 @@ "mini-css-extract-plugin": "^2.8.1", "npm-run-all": "^4.1.5", "patch-package": "^6.5.1", + "playwright": "^1.42.1", "rimraf": "^4.4.1", "style-loader": "^3.3.4", "terser-webpack-plugin": "^5.3.10", @@ -3382,6 +3384,21 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", + "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "dev": true, + "dependencies": { + "playwright": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -20848,6 +20865,36 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", + "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "dev": true, + "dependencies": { + "playwright-core": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", + "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/playwright-test": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/playwright-test/-/playwright-test-14.1.0.tgz", @@ -21143,6 +21190,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/please-upgrade-node": { "version": "3.2.0", "dev": true, diff --git a/package.json b/package.json index a0b5f331..bd3d21f9 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,11 @@ "serve": "webpack serve --mode=development", "serve:prod": "webpack serve --mode=production", "start": "npm run serve", - "test": "aegir test -f dist-tsc/test/**/*.spec.js", - "test:node": "aegir test -t node -f dist-tsc/test/node.js", + "test": "aegir test -f dist-tsc/test/**/*.spec.js --cov", + "test:browsers": "playwright test -c playwright.config.js", + "test:chrome": "playwright test -c playwright.config.js --project chromium", + "test:firefox": "playwright test -c playwright.config.js --project firefox", + "test:node": "aegir test -t node -f dist-tsc/test/node.js --cov", "postinstall": "patch-package" }, "browser": "./dist/src/index.js", @@ -49,6 +52,7 @@ "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", + "@playwright/test": "^1.42.1", "@types/react": "^18.0.31", "aegir": "^42.2.2", "babel-loader": "^9.1.3", @@ -61,6 +65,7 @@ "mini-css-extract-plugin": "^2.8.1", "npm-run-all": "^4.1.5", "patch-package": "^6.5.1", + "playwright": "^1.42.1", "rimraf": "^4.4.1", "style-loader": "^3.3.4", "terser-webpack-plugin": "^5.3.10", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 00000000..8f649ccb --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,47 @@ +import { defineConfig, devices } from '@playwright/test' + +export default defineConfig({ + testDir: './test-e2e', + testMatch: /(.+\.)?(test|spec)\.[jt]s/, + /* Run tests in files in parallel */ + fullyParallel: true, + /* 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 != null) ? 2 : 0, + /** + * Opt out of parallel tests by setting workers to 1. + * We don't want to bombard Helia gateway with parallel requests, it's not ready for that yet. + */ + workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + + // 'allow' serviceWorkers is the default, but we want to be explicit + serviceWorkers: 'allow' + }, + globalSetup: './test-e2e/global-setup.js', + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] } + } + ], + webServer: { + command: 'npm run start', + port: 3000, + timeout: 120 * 1000, + reuseExistingServer: !process.env.CI + } +}) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 0e74f9a7..fb06844b 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -7,12 +7,12 @@ export default function Header (): JSX.Element { const { isConfigExpanded, setConfigExpanded } = useContext(ConfigContext) return ( -
+
IPFS logo - IPFS Service Worker Gateway -