stacker.news/lib/apollo.js
ekzyis dde82e25a5
Convert worker to ESM (#500)
* Convert worker to ESM

To use ESM for the worker, I created a package.json file in worker/ with `{ type: "module" }` as its sole content.

I then rewrote every import to use ESM syntax.

I also tried to set `{ type: "module" }` in the root package.json file to also use ESM in next.config.js.

However, this resulted in a weird problem: default imports were now getting imported as objects in this shape: `{ default: <defaultImport> }`.

Afaik, this should only be the case if you use "import * as foo from 'bar'" syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#default_import

This is fixed by not using `{ type: "module" }` for some reason. However, then, next.config.js also doesn't support ESM import syntax anymore.

The documentation says that if you want to use ESM, you can use next.config.mjs: https://nextjs.org/docs/pages/api-reference/next-config-js

But I didn't want to use MJS extension since I don't have any experience with it. For example, not sure if it's good style to mix JS with MJS etc. So I kept the CJS import syntax there.

* Ignore worker/ during linting

I wasn't able to fix the following errors:

/home/runner/work/stacker.news/stacker.news/worker/auction.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/auction.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/earn.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/earn.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/index.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/index.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/nostr.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/nostr.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/ots.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/ots.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/repin.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/repin.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/search.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/search.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/streak.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/streak.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/trust.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/trust.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/views.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/views.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/wallet.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/wallet.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)

I tried to tell babel where to find the babel config file (.babelrc), specifying the babel config in worker/package.json under "babel", using babel.config.json etc. to no avail.

However, afaict, we don't need babel for the worker since it won't run in a browser. Babel is only used to transpile code to target browsers.

But it still would be nice to lint the worker code with standard.

But we can figure this out later.

* Fix worker imports from lib/ and api/

This fixes the issue that we can't use `{ "type": "module" }` in the root package.json since it breaks the app with this error:

  app  | TypeError: next_auth_providers_credentials__WEBPACK_IMPORTED_MODULE_2__ is not a function
  app  |     at eval (webpack-internal:///./pages/api/auth/[...nextauth].js:218:20)
  app  |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
  app  | LND GRPC connection successful
  app  | - error pages/api/auth/[...nextauth].js (139:2) @ CredentialsProvider
  app  | - error Error [TypeError]: next_auth_providers_credentials__WEBPACK_IMPORTED_MODULE_2__ is not a function
  app  |     at eval (webpack-internal:///./pages/api/auth/[...nextauth].js:218:20) {
  app  |   digest: undefined
  app  | }
  app  |   137 |
  app  |   138 | const providers = [
  app  | > 139 |   CredentialsProvider({
  app  |       |  ^
  app  |   140 |     id: 'lightning',
  app  |   141 |     name: 'Lightning',
  app  |   142 |     credentials: {
  app  | TypeError: next_auth_providers_credentials__WEBPACK_IMPORTED_MODULE_2__ is not a function
  app  |     at eval (webpack-internal:///./pages/api/auth/[...nextauth].js:218:20)
  app  |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

build but we need to tell the worker that the files are MJS, else we get this error:

  worker  | file:///app/worker/wallet.js:3
  worker  | import { datePivot } from '../lib/time.js'
  worker  |          ^^^^^^^^^
  worker  | SyntaxError: Named export 'datePivot' not found. The requested module '../lib/time.js' is a CommonJS module, which may not support all module.exports as named exports.
  worker  | CommonJS modules can always be imported via the default export, for example using:
  worker  |
  worker  | import pkg from '../lib/time.js';
  worker  | const { datePivot } = pkg;
  worker  |
  worker  |     at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
  worker  |     at async ModuleJob.run (node:internal/modules/esm/module_job:190:5)
  worker  |
  worker  | Node.js v18.17.0
  worker  |
  worker exited with code 1

* Fix global not defined in browser context

* Also ignore api/ and lib/ during linting

I did not want to do this but I was not able to fix this error in any other way I tried:

  /home/ekzyis/programming/stacker.news/api/lnd/index.js:0:0: Parsing error: No Babel config file detected for /home/ekzyis/programming/stacker.news/api/lnd/index.js. Either disable config file checking with requ
ireConfigFile: false, or configure Babel so that it can find the config files. (null)

Did not want to look deeper into all this standard, eslint, babel configuration stuff ...

---------

Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2023-09-23 20:19:35 -05:00

177 lines
5.4 KiB
JavaScript

import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client'
import { decodeCursor, LIMIT } from './cursor'
import { SSR } from './constants'
function isFirstPage (cursor, existingThings) {
if (cursor) {
const decursor = decodeCursor(cursor)
return decursor.offset === LIMIT
} else {
// we don't have anything cached, or our existing items are less than
// or equal to a full page
return existingThings?.length < LIMIT
}
}
const defaultFetchPolicy = SSR ? 'cache-only' : 'cache-first'
const defaultNextFetchPolicy = SSR ? 'cache-only' : 'cache-first'
export default function getApolloClient () {
if (SSR) {
return getClient(`${process.env.SELF_URL}/api/graphql`)
} else {
window.apolloClient ||= getClient('/api/graphql')
return window.apolloClient
}
}
function getClient (uri) {
return new ApolloClient({
link: new HttpLink({ uri }),
ssrMode: SSR,
cache: new InMemoryCache({
freezeResults: true,
typePolicies: {
Query: {
fields: {
topUsers: {
keyArgs: ['when', 'by'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.users)) {
return incoming
}
return {
cursor: incoming.cursor,
users: [...(existing?.users || []), ...incoming.users]
}
}
},
topCowboys: {
keyArgs: [],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.users)) {
return incoming
}
return {
cursor: incoming.cursor,
users: [...(existing?.users || []), ...incoming.users]
}
}
},
items: {
keyArgs: ['sub', 'sort', 'type', 'name', 'when', 'by'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.items)) {
return incoming
}
return {
cursor: incoming.cursor,
items: [...(existing?.items || []), ...incoming.items],
pins: existing?.pins || null
}
}
},
comments: {
keyArgs: ['id', 'sort'],
merge (existing, incoming) {
return incoming
}
},
related: {
keyArgs: ['id', 'title', 'minMatch', 'limit'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.items)) {
return incoming
}
return {
cursor: incoming.cursor,
items: [...(existing?.items || []), ...incoming.items]
}
}
},
search: {
keyArgs: ['q', 'sub', 'sort', 'what', 'when'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.items)) {
return incoming
}
return {
cursor: incoming.cursor,
items: [...(existing?.items || []), ...incoming.items]
}
}
},
searchUsers: {
keyArgs: ['q', 'limit', 'similarity'],
merge (existing, incoming) {
return [...(existing?.searchUsers || []), ...incoming]
}
},
notifications: {
keyArgs: ['inc'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.notifications)) {
return incoming
}
return {
cursor: incoming.cursor,
earn: existing?.earn || incoming.earn,
notifications: [...(existing?.notifications || []), ...incoming.notifications],
lastChecked: existing?.lastChecked || incoming.lastChecked
}
}
},
walletHistory: {
keyArgs: ['inc'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.facts)) {
return incoming
}
return {
cursor: incoming.cursor,
facts: [...(existing?.facts || []), ...incoming.facts]
}
}
}
}
},
Item: {
fields: {
meAnonSats: {
read (meAnonSats, { readField }) {
if (typeof window === 'undefined') return null
const itemId = readField('id')
return meAnonSats ?? Number(window.localStorage.getItem(`TIP-item:${itemId}`) || '0')
}
}
}
}
}
}),
assumeImmutableResults: true,
defaultOptions: {
watchQuery: {
initialFetchPolicy: defaultFetchPolicy,
fetchPolicy: defaultFetchPolicy,
nextFetchPolicy: defaultNextFetchPolicy,
canonizeResults: true,
ssr: SSR
},
query: {
initialFetchPolicy: defaultFetchPolicy,
fetchPolicy: defaultFetchPolicy,
nextFetchPolicy: defaultNextFetchPolicy,
canonizeResults: true,
ssr: SSR
}
}
})
}