├── .editorconfig ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .tool-versions ├── LICENSE ├── README.md ├── examples └── books-browser-javascript │ ├── .gitignore │ ├── README.md │ ├── esbuild.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── index.html │ └── site.webmanifest │ ├── relay.config.js │ ├── schema.graphql │ └── src │ ├── App.jsx │ ├── BookList.jsx │ ├── BookListItem.jsx │ ├── data.js │ ├── index.jsx │ └── server.js ├── jest.config.js ├── package-lock.json ├── package.json ├── scripts └── build.cjs ├── src ├── __tests__ │ ├── __snapshots__ │ │ └── compile.test.ts.snap │ ├── compile.test.ts │ ├── fixtures │ │ ├── relay-config-file │ │ │ └── relay.config.js │ │ └── relay-config-typescript │ │ │ └── package.json │ └── setup.test.ts ├── compile.ts ├── index.cjs.ts ├── index.ts └── setup.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} 8 | 9 | runs-on: ${{ matrix.os }} 10 | strategy: 11 | matrix: 12 | node: ["16.x", "18.x", "19.x"] 13 | os: [ubuntu-latest] 14 | 15 | steps: 16 | - name: Checkout repo 17 | uses: actions/checkout@v2 18 | 19 | - name: Use Node ${{ matrix.node }} 20 | uses: actions/setup-node@v2 21 | with: 22 | node-version: ${{ matrix.node }} 23 | 24 | - name: Cache dependencies 25 | uses: actions/cache@v2 26 | with: 27 | path: ~/.npm 28 | key: npm-${{ hashFiles('package-lock.json') }} 29 | restore-keys: npm- 30 | 31 | - name: Install dependencies 32 | run: npm ci 33 | 34 | #- name: Lint 35 | # run: npm run lint 36 | 37 | - name: Test 38 | run: npm run test 39 | 40 | - name: Build 41 | run: npm run build 42 | 43 | - name: Verify 44 | run: | 45 | echo "require('assert').ok(typeof require('./lib/index.cjs') == 'function')" | node --input-type="commonjs" 46 | echo "import { ok } from 'assert'; import relay from './lib/index.mjs'; ok(typeof relay === 'function');" | node --input-type="module" 47 | 48 | - name: Lint package 49 | run: npx publint 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 18.13.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2023 smartvokat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esbuild-plugin-relay 2 | 3 | [![Continuous Integration](https://github.com/smartvokat/esbuild-plugin-relay/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/smartvokat/esbuild-plugin-relay/actions/workflows/ci.yaml) 4 | 5 | An [esbuild](https://esbuild.github.io/) plugin to transform tagged GraphQL template literals for [Relay](https://relay.dev/). Heavily inspired by [babel-plugin-relay](https://www.npmjs.com/package/babel-plugin-relay). 6 | 7 | ## Features 8 | 9 | - Replaces `graphql` tagged template literals with imports to the artifacts generated by `relay-compiler` 10 | - Supports CommonJS and ESM module formats 11 | - Supports TypeScript projects 12 | - Optionally warns you about outdated operations and fragments 13 | 14 | ## Caveat 15 | 16 | This plugin uses string replacements and **does not parse** the source code. This means it will try to compile GraphQL literals even if they are commented out. If that does not fit your workflow, you'll need to use esbuild + babel + babel-plugin-relay. 17 | 18 | ## Installation 19 | 20 | ```sh 21 | # Using NPM 22 | npm install -D esbuild graphql esbuild-plugin-relay 23 | 24 | # Using yarn 25 | yarn add -D esbuild graphql esbuild-plugin-relay 26 | ``` 27 | 28 | ## Usage 29 | 30 | ```js 31 | // esbuild.config.js 32 | import esbuild from "esbuild"; 33 | import relay from "esbuild-plugin-relay"; 34 | 35 | esbuild.build({ 36 | // ...other options, 37 | plugins: [relay()], 38 | }); 39 | ``` 40 | 41 | ## Acknowledgements 42 | 43 | This plugin is based on the official [babel-plugin-relay](https://www.npmjs.com/package/babel-plugin-relay). 44 | 45 | [The Gist by Samuel Cormier-Iijima](https://gist.github.com/sciyoshi/34e5865f2523848f0d60b4cdd49382ee) was a great starting point. 46 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | src/__generated__ 4 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/README.md: -------------------------------------------------------------------------------- 1 | # Books Browser in JavaScript 2 | 3 | This is an example to show how to use `esbuild-plugin-relay` in a minimal JavaScript application. 4 | 5 | ## Installation 6 | 7 | ``` 8 | cd examples/books-browser-javascript 9 | npm install 10 | npm run relay 11 | npm run start:server 12 | ``` 13 | 14 | Open another terminal session and start the development server: 15 | 16 | ``` 17 | npm run start 18 | ``` 19 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/esbuild.config.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const esbuild = require("esbuild"); 3 | const fs = require("fs-extra"); 4 | const path = require("path"); 5 | const relay = require("esbuild-plugin-relay"); 6 | 7 | const root = path.resolve(__dirname); 8 | const outDir = path.resolve(root, "dist"); 9 | const publicDir = path.resolve(root, "public"); 10 | 11 | let config = "-build"; 12 | if (process.argv.length > 2) { 13 | config = process.argv[2]; 14 | } 15 | 16 | fs.ensureDirSync(outDir); 17 | fs.emptyDirSync(outDir); 18 | fs.copySync(publicDir, outDir); 19 | 20 | const baseConfig = { 21 | entryPoints: ["src/index.jsx"], 22 | outdir: outDir, 23 | bundle: true, 24 | define: { 25 | "process.env.NODE_ENV": JSON.stringify( 26 | process.env.NODE_ENV || "production" 27 | ), 28 | global: "window", 29 | }, 30 | sourcemap: true, 31 | minify: false, 32 | plugins: [relay()], 33 | }; 34 | 35 | try { 36 | if (config === "-watch") { 37 | esbuild.build({ ...baseConfig, watch: true }); 38 | serve(); 39 | } 40 | 41 | if (config === "-build") { 42 | console.log("Building…"); 43 | esbuild.build({ 44 | ...baseConfig, 45 | minify: true, 46 | }); 47 | } 48 | } catch (err) { 49 | process.exit(1); 50 | } 51 | 52 | async function serve() { 53 | const port = process.env.PORT || "4000"; 54 | 55 | console.log(`Development server running at http://localhost:${port}/`); 56 | const servor = require("servor"); 57 | await servor({ 58 | browser: true, 59 | root: outDir, 60 | port, 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-plugin-relay-simple-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node ./esbuild.config.js -watch", 8 | "start:server": "node ./src/server.js", 9 | "relay": "relay-compiler --src ./src --schema ./schema.graphql" 10 | }, 11 | "license": "MIT", 12 | "private": true, 13 | "dependencies": { 14 | "apollo-server": "^3.5.0", 15 | "esbuild-plugin-relay": "file:../..", 16 | "graphql": "^15.7.2", 17 | "react": "^17.0.2", 18 | "react-dom": "^17.0.2", 19 | "react-relay": "^12.0.0", 20 | "relay-config": "^12.0.0" 21 | }, 22 | "devDependencies": { 23 | "fs-extra": "^10.0.0", 24 | "relay-compiler": "^12.0.0", 25 | "servor": "^4.0.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartvokat/esbuild-plugin-relay/b05bebc17cce36d84b245a4553ca4ae418184a30/examples/books-browser-javascript/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartvokat/esbuild-plugin-relay/b05bebc17cce36d84b245a4553ca4ae418184a30/examples/books-browser-javascript/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartvokat/esbuild-plugin-relay/b05bebc17cce36d84b245a4553ca4ae418184a30/examples/books-browser-javascript/public/apple-touch-icon.png -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartvokat/esbuild-plugin-relay/b05bebc17cce36d84b245a4553ca4ae418184a30/examples/books-browser-javascript/public/favicon-16x16.png -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartvokat/esbuild-plugin-relay/b05bebc17cce36d84b245a4553ca4ae418184a30/examples/books-browser-javascript/public/favicon-32x32.png -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartvokat/esbuild-plugin-relay/b05bebc17cce36d84b245a4553ca4ae418184a30/examples/books-browser-javascript/public/favicon.ico -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Books Browser 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /examples/books-browser-javascript/relay.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | src: "./src", 3 | schema: "./schema.graphql", 4 | exclude: ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"], 5 | }; 6 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | Exposes a URL that specifies the behaviour of this scalar. 3 | """ 4 | directive @specifiedBy( 5 | """ 6 | The URL that specifies the behaviour of this scalar. 7 | """ 8 | url: String! 9 | ) on SCALAR 10 | 11 | type Book { 12 | id: String! 13 | title: String! 14 | } 15 | 16 | type Query { 17 | books(first: Int): [Book] 18 | } 19 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/src/App.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { BookList } from "./BookList"; 3 | 4 | export function App() { 5 | return ( 6 |
7 |

Books

8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/src/BookList.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { graphql, useLazyLoadQuery } from "react-relay/hooks"; 3 | import { BookListItem } from "./BookListItem"; 4 | 5 | export function BookList() { 6 | const data = useLazyLoadQuery( 7 | graphql` 8 | query BookListQuery { 9 | books(first: 100) { 10 | id 11 | ...BookListItem_book 12 | } 13 | } 14 | ` 15 | ); 16 | 17 | return ( 18 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/src/BookListItem.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { graphql, useFragment } from "react-relay/hooks"; 3 | 4 | export function BookListItem({ book }) { 5 | const data = useFragment(BookListItem_book, book); 6 | return
  • {data.title}
  • ; 7 | } 8 | 9 | const BookListItem_book = graphql` 10 | fragment BookListItem_book on Book { 11 | id 12 | title 13 | } 14 | `; 15 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/src/data.js: -------------------------------------------------------------------------------- 1 | // Sourced from http://gutendex.com/ on 2021-12-06 2 | module.exports = [ 3 | { 4 | id: 84, 5 | title: "Frankenstein; Or, The Modern Prometheus", 6 | authors: [ 7 | { 8 | name: "Shelley, Mary Wollstonecraft", 9 | birth_year: 1797, 10 | death_year: 1851, 11 | }, 12 | ], 13 | translators: [], 14 | subjects: [ 15 | "Frankenstein's monster (Fictitious character) -- Fiction", 16 | "Frankenstein, Victor (Fictitious character) -- Fiction", 17 | "Gothic fiction", 18 | "Horror tales", 19 | "Monsters -- Fiction", 20 | "Science fiction", 21 | "Scientists -- Fiction", 22 | ], 23 | bookshelves: [ 24 | "Gothic Fiction", 25 | "Movie Books", 26 | "Precursors of Science Fiction", 27 | "Science Fiction by Women", 28 | ], 29 | languages: ["en"], 30 | copyright: false, 31 | media_type: "Text", 32 | formats: { 33 | "application/epub+zip": "https://www.gutenberg.org/ebooks/84.epub.images", 34 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/84.rdf", 35 | "application/x-mobipocket-ebook": 36 | "https://www.gutenberg.org/ebooks/84.kindle.images", 37 | "application/zip": "https://www.gutenberg.org/files/84/84-0.zip", 38 | "image/jpeg": 39 | "https://www.gutenberg.org/cache/epub/84/pg84.cover.small.jpg", 40 | "text/plain; charset=utf-8": 41 | "https://www.gutenberg.org/files/84/84-0.txt", 42 | "text/html; charset=utf-8": "https://www.gutenberg.org/files/84/84-h.zip", 43 | "text/html": "https://www.gutenberg.org/ebooks/84.html.images", 44 | }, 45 | download_count: 84371, 46 | }, 47 | { 48 | id: 1342, 49 | title: "Pride and Prejudice", 50 | authors: [ 51 | { 52 | name: "Austen, Jane", 53 | birth_year: 1775, 54 | death_year: 1817, 55 | }, 56 | ], 57 | translators: [], 58 | subjects: [ 59 | "Courtship -- Fiction", 60 | "Domestic fiction", 61 | "England -- Fiction", 62 | "Love stories", 63 | "Sisters -- Fiction", 64 | "Social classes -- Fiction", 65 | "Young women -- Fiction", 66 | ], 67 | bookshelves: ["Best Books Ever Listings", "Harvard Classics"], 68 | languages: ["en"], 69 | copyright: false, 70 | media_type: "Text", 71 | formats: { 72 | "text/html; charset=utf-8": 73 | "https://www.gutenberg.org/files/1342/1342-h/1342-h.htm", 74 | "application/x-mobipocket-ebook": 75 | "https://www.gutenberg.org/ebooks/1342.kindle.images", 76 | "application/epub+zip": 77 | "https://www.gutenberg.org/ebooks/1342.epub.images", 78 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1342.rdf", 79 | "application/zip": "https://www.gutenberg.org/files/1342/1342-h.zip", 80 | "text/plain; charset=utf-8": 81 | "https://www.gutenberg.org/files/1342/1342-0.zip", 82 | "image/jpeg": 83 | "https://www.gutenberg.org/cache/epub/1342/pg1342.cover.medium.jpg", 84 | "text/html": "https://www.gutenberg.org/ebooks/1342.html.images", 85 | }, 86 | download_count: 56860, 87 | }, 88 | { 89 | id: 25344, 90 | title: "The Scarlet Letter", 91 | authors: [ 92 | { 93 | name: "Hawthorne, Nathaniel", 94 | birth_year: 1804, 95 | death_year: 1864, 96 | }, 97 | ], 98 | translators: [], 99 | subjects: [ 100 | "Adultery -- Fiction", 101 | "Boston (Mass.) -- History -- Colonial period, ca. 1600-1775 -- Fiction", 102 | "Clergy -- Fiction", 103 | "Historical fiction", 104 | "Illegitimate children -- Fiction", 105 | "Married women -- Fiction", 106 | "Psychological fiction", 107 | "Puritans -- Fiction", 108 | "Revenge -- Fiction", 109 | "Triangles (Interpersonal relations) -- Fiction", 110 | "Women immigrants -- Fiction", 111 | ], 112 | bookshelves: ["Banned Books from Anne Haight's list"], 113 | languages: ["en"], 114 | copyright: false, 115 | media_type: "Text", 116 | formats: { 117 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/25344.rdf", 118 | "application/x-mobipocket-ebook": 119 | "https://www.gutenberg.org/ebooks/25344.kindle.images", 120 | "application/epub+zip": 121 | "https://www.gutenberg.org/ebooks/25344.epub.images", 122 | "text/plain": "https://www.gutenberg.org/ebooks/25344.txt.utf-8", 123 | "text/plain; charset=us-ascii": 124 | "https://www.gutenberg.org/files/25344/25344-0.txt", 125 | "application/octet-stream": 126 | "https://www.gutenberg.org/files/25344/25344-h.zip", 127 | "application/zip": "https://www.gutenberg.org/files/25344/25344-0.zip", 128 | "image/jpeg": 129 | "https://www.gutenberg.org/cache/epub/25344/pg25344.cover.small.jpg", 130 | "text/html": "https://www.gutenberg.org/files/25344/25344-h/25344-h.htm", 131 | }, 132 | download_count: 39513, 133 | }, 134 | { 135 | id: 46, 136 | title: "A Christmas Carol in Prose; Being a Ghost Story of Christmas", 137 | authors: [ 138 | { 139 | name: "Dickens, Charles", 140 | birth_year: 1812, 141 | death_year: 1870, 142 | }, 143 | ], 144 | translators: [], 145 | subjects: [ 146 | "Christmas stories", 147 | "Ghost stories", 148 | "London (England) -- Fiction", 149 | "Misers -- Fiction", 150 | "Poor families -- Fiction", 151 | "Scrooge, Ebenezer (Fictitious character) -- Fiction", 152 | "Sick children -- Fiction", 153 | ], 154 | bookshelves: ["Children's Literature", "Christmas"], 155 | languages: ["en"], 156 | copyright: false, 157 | media_type: "Text", 158 | formats: { 159 | "application/epub+zip": "https://www.gutenberg.org/ebooks/46.epub.images", 160 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/46.rdf", 161 | "application/x-mobipocket-ebook": 162 | "https://www.gutenberg.org/ebooks/46.kindle.images", 163 | "text/html": "https://www.gutenberg.org/ebooks/46.html.images", 164 | "text/plain; charset=us-ascii": 165 | "https://www.gutenberg.org/files/46/46-0.txt", 166 | "application/octet-stream": "https://www.gutenberg.org/files/46/46-h.zip", 167 | "text/plain": "https://www.gutenberg.org/ebooks/46.txt.utf-8", 168 | "image/jpeg": 169 | "https://www.gutenberg.org/cache/epub/46/pg46.cover.medium.jpg", 170 | }, 171 | download_count: 37394, 172 | }, 173 | { 174 | id: 11, 175 | title: "Alice's Adventures in Wonderland", 176 | authors: [ 177 | { 178 | name: "Carroll, Lewis", 179 | birth_year: 1832, 180 | death_year: 1898, 181 | }, 182 | ], 183 | translators: [], 184 | subjects: [ 185 | "Alice (Fictitious character from Carroll) -- Juvenile fiction", 186 | "Children's stories", 187 | "Fantasy fiction", 188 | "Imaginary places -- Juvenile fiction", 189 | ], 190 | bookshelves: ["Children's Literature"], 191 | languages: ["en"], 192 | copyright: false, 193 | media_type: "Text", 194 | formats: { 195 | "image/jpeg": 196 | "https://www.gutenberg.org/cache/epub/11/pg11.cover.medium.jpg", 197 | "application/epub+zip": "https://www.gutenberg.org/ebooks/11.epub.images", 198 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/11.rdf", 199 | "text/html; charset=utf-8": 200 | "https://www.gutenberg.org/files/11/11-h/11-h.htm", 201 | "application/x-mobipocket-ebook": 202 | "https://www.gutenberg.org/ebooks/11.kindle.images", 203 | "application/zip": "https://www.gutenberg.org/files/11/11-h.zip", 204 | "text/plain; charset=utf-8": 205 | "https://www.gutenberg.org/files/11/11-0.zip", 206 | "text/html": "https://www.gutenberg.org/ebooks/11.html.images", 207 | }, 208 | download_count: 27444, 209 | }, 210 | { 211 | id: 345, 212 | title: "Dracula", 213 | authors: [ 214 | { 215 | name: "Stoker, Bram", 216 | birth_year: 1847, 217 | death_year: 1912, 218 | }, 219 | ], 220 | translators: [], 221 | subjects: [ 222 | "Dracula, Count (Fictitious character) -- Fiction", 223 | "Epistolary fiction", 224 | "Gothic fiction", 225 | "Horror tales", 226 | "Transylvania (Romania) -- Fiction", 227 | "Vampires -- Fiction", 228 | "Whitby (England) -- Fiction", 229 | ], 230 | bookshelves: ["Gothic Fiction", "Horror", "Movie Books", "Mystery Fiction"], 231 | languages: ["en"], 232 | copyright: false, 233 | media_type: "Text", 234 | formats: { 235 | "image/jpeg": 236 | "https://www.gutenberg.org/cache/epub/345/pg345.cover.small.jpg", 237 | "application/epub+zip": 238 | "https://www.gutenberg.org/ebooks/345.epub.images", 239 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/345.rdf", 240 | "application/x-mobipocket-ebook": 241 | "https://www.gutenberg.org/ebooks/345.kindle.images", 242 | "text/html; charset=utf-8": 243 | "https://www.gutenberg.org/files/345/345-h.zip", 244 | "text/html": "https://www.gutenberg.org/ebooks/345.html.images", 245 | "application/zip": "https://www.gutenberg.org/files/345/345-0.zip", 246 | "text/plain; charset=utf-8": 247 | "https://www.gutenberg.org/files/345/345-0.txt", 248 | }, 249 | download_count: 25193, 250 | }, 251 | { 252 | id: 2701, 253 | title: "Moby Dick; Or, The Whale", 254 | authors: [ 255 | { 256 | name: "Melville, Herman", 257 | birth_year: 1819, 258 | death_year: 1891, 259 | }, 260 | ], 261 | translators: [], 262 | subjects: [ 263 | "Adventure stories", 264 | "Ahab, Captain (Fictitious character) -- Fiction", 265 | "Mentally ill -- Fiction", 266 | "Psychological fiction", 267 | "Sea stories", 268 | "Ship captains -- Fiction", 269 | "Whales -- Fiction", 270 | "Whaling -- Fiction", 271 | "Whaling ships -- Fiction", 272 | ], 273 | bookshelves: ["Best Books Ever Listings"], 274 | languages: ["en"], 275 | copyright: false, 276 | media_type: "Text", 277 | formats: { 278 | "application/x-mobipocket-ebook": 279 | "https://www.gutenberg.org/ebooks/2701.kindle.images", 280 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/2701.rdf", 281 | "application/epub+zip": 282 | "https://www.gutenberg.org/ebooks/2701.epub.images", 283 | "text/plain; charset=utf-8": 284 | "https://www.gutenberg.org/files/2701/2701-0.txt", 285 | "text/html; charset=utf-8": 286 | "https://www.gutenberg.org/files/2701/2701-h.zip", 287 | "image/jpeg": 288 | "https://www.gutenberg.org/cache/epub/2701/pg2701.cover.small.jpg", 289 | "text/html": "https://www.gutenberg.org/ebooks/2701.html.images", 290 | }, 291 | download_count: 23697, 292 | }, 293 | { 294 | id: 2542, 295 | title: "A Doll's House : a play", 296 | authors: [ 297 | { 298 | name: "Ibsen, Henrik", 299 | birth_year: 1828, 300 | death_year: 1906, 301 | }, 302 | ], 303 | translators: [], 304 | subjects: [ 305 | "Man-woman relationships -- Drama", 306 | "Marriage -- Drama", 307 | "Norwegian drama -- Translations into English", 308 | "Wives -- Drama", 309 | ], 310 | bookshelves: ["Best Books Ever Listings"], 311 | languages: ["en"], 312 | copyright: false, 313 | media_type: "Text", 314 | formats: { 315 | "text/plain; charset=utf-8": 316 | "https://www.gutenberg.org/files/2542/2542-0.txt", 317 | "text/html; charset=utf-8": 318 | "https://www.gutenberg.org/files/2542/2542-h/2542-h.htm", 319 | "application/x-mobipocket-ebook": 320 | "https://www.gutenberg.org/ebooks/2542.kindle.images", 321 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/2542.rdf", 322 | "application/epub+zip": 323 | "https://www.gutenberg.org/ebooks/2542.epub.images", 324 | "image/jpeg": 325 | "https://www.gutenberg.org/cache/epub/2542/pg2542.cover.small.jpg", 326 | "application/zip": "https://www.gutenberg.org/files/2542/2542-0.zip", 327 | "text/html": "https://www.gutenberg.org/ebooks/2542.html.images", 328 | }, 329 | download_count: 22364, 330 | }, 331 | { 332 | id: 174, 333 | title: "The Picture of Dorian Gray", 334 | authors: [ 335 | { 336 | name: "Wilde, Oscar", 337 | birth_year: 1854, 338 | death_year: 1900, 339 | }, 340 | ], 341 | translators: [], 342 | subjects: [ 343 | "Appearance (Philosophy) -- Fiction", 344 | "Conduct of life -- Fiction", 345 | "Didactic fiction", 346 | "Great Britain -- History -- Victoria, 1837-1901 -- Fiction", 347 | "London (England) -- History -- 1800-1950 -- Fiction", 348 | "Paranormal fiction", 349 | "Portraits -- Fiction", 350 | "Supernatural -- Fiction", 351 | ], 352 | bookshelves: ["Gothic Fiction", "Movie Books"], 353 | languages: ["en"], 354 | copyright: false, 355 | media_type: "Text", 356 | formats: { 357 | "application/epub+zip": 358 | "https://www.gutenberg.org/ebooks/174.epub.images", 359 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/174.rdf", 360 | "application/x-mobipocket-ebook": 361 | "https://www.gutenberg.org/ebooks/174.kindle.images", 362 | "text/plain; charset=utf-8": 363 | "https://www.gutenberg.org/files/174/174-0.zip", 364 | "text/html; charset=iso-8859-1": 365 | "https://www.gutenberg.org/files/174/174-h.zip", 366 | "text/html": "https://www.gutenberg.org/ebooks/174.html.images", 367 | "image/jpeg": 368 | "https://www.gutenberg.org/cache/epub/174/pg174.cover.medium.jpg", 369 | }, 370 | download_count: 22149, 371 | }, 372 | { 373 | id: 1661, 374 | title: "The Adventures of Sherlock Holmes", 375 | authors: [ 376 | { 377 | name: "Doyle, Arthur Conan", 378 | birth_year: 1859, 379 | death_year: 1930, 380 | }, 381 | ], 382 | translators: [], 383 | subjects: [ 384 | "Detective and mystery stories, English", 385 | "Holmes, Sherlock (Fictitious character) -- Fiction", 386 | "Private investigators -- England -- Fiction", 387 | ], 388 | bookshelves: [ 389 | "Banned Books from Anne Haight's list", 390 | "Contemporary Reviews", 391 | "Detective Fiction", 392 | ], 393 | languages: ["en"], 394 | copyright: false, 395 | media_type: "Text", 396 | formats: { 397 | "application/epub+zip": 398 | "https://www.gutenberg.org/ebooks/1661.epub.images", 399 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1661.rdf", 400 | "application/x-mobipocket-ebook": 401 | "https://www.gutenberg.org/ebooks/1661.kindle.images", 402 | "text/html; charset=utf-8": 403 | "https://www.gutenberg.org/files/1661/1661-h/1661-h.htm", 404 | "image/jpeg": 405 | "https://www.gutenberg.org/cache/epub/1661/pg1661.cover.small.jpg", 406 | "text/plain; charset=utf-8": 407 | "https://www.gutenberg.org/files/1661/1661-0.zip", 408 | "application/zip": "https://www.gutenberg.org/files/1661/1661-h.zip", 409 | "text/html": "https://www.gutenberg.org/ebooks/1661.html.images", 410 | }, 411 | download_count: 21158, 412 | }, 413 | { 414 | id: 1080, 415 | title: 416 | "A Modest Proposal: For preventing the children of poor people in Ireland, from being a burden on their parents or country, and for making them beneficial to the publick", 417 | authors: [ 418 | { 419 | name: "Swift, Jonathan", 420 | birth_year: 1667, 421 | death_year: 1745, 422 | }, 423 | ], 424 | translators: [], 425 | subjects: [ 426 | "Ireland -- Politics and government -- 18th century -- Humor", 427 | "Political satire, English", 428 | "Religious satire, English", 429 | ], 430 | bookshelves: [], 431 | languages: ["en"], 432 | copyright: false, 433 | media_type: "Text", 434 | formats: { 435 | "application/x-mobipocket-ebook": 436 | "https://www.gutenberg.org/ebooks/1080.kindle.images", 437 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1080.rdf", 438 | "application/epub+zip": 439 | "https://www.gutenberg.org/ebooks/1080.epub.images", 440 | "application/zip": "https://www.gutenberg.org/files/1080/1080-h.zip", 441 | "image/jpeg": 442 | "https://www.gutenberg.org/cache/epub/1080/pg1080.cover.small.jpg", 443 | "text/plain; charset=utf-8": 444 | "https://www.gutenberg.org/files/1080/1080-0.zip", 445 | "text/html": "https://www.gutenberg.org/ebooks/1080.html.images", 446 | "text/html; charset=utf-8": 447 | "https://www.gutenberg.org/files/1080/1080-h/1080-h.htm", 448 | }, 449 | download_count: 20559, 450 | }, 451 | { 452 | id: 43, 453 | title: "The Strange Case of Dr. Jekyll and Mr. Hyde", 454 | authors: [ 455 | { 456 | name: "Stevenson, Robert Louis", 457 | birth_year: 1850, 458 | death_year: 1894, 459 | }, 460 | ], 461 | translators: [], 462 | subjects: [ 463 | "Horror tales", 464 | "London (England) -- Fiction", 465 | "Multiple personality -- Fiction", 466 | "Physicians -- Fiction", 467 | "Psychological fiction", 468 | "Science fiction", 469 | "Self-experimentation in medicine -- Fiction", 470 | ], 471 | bookshelves: ["Horror", "Movie Books", "Precursors of Science Fiction"], 472 | languages: ["en"], 473 | copyright: false, 474 | media_type: "Text", 475 | formats: { 476 | "application/epub+zip": "https://www.gutenberg.org/ebooks/43.epub.images", 477 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/43.rdf", 478 | "application/x-mobipocket-ebook": 479 | "https://www.gutenberg.org/ebooks/43.kindle.images", 480 | "application/zip": "https://www.gutenberg.org/files/43/43-h.zip", 481 | "text/html; charset=utf-8": 482 | "https://www.gutenberg.org/files/43/43-h/43-h.htm", 483 | "image/jpeg": 484 | "https://www.gutenberg.org/cache/epub/43/pg43.cover.medium.jpg", 485 | "text/html": "https://www.gutenberg.org/ebooks/43.html.images", 486 | "text/plain; charset=utf-8": 487 | "https://www.gutenberg.org/files/43/43-0.txt", 488 | }, 489 | download_count: 18756, 490 | }, 491 | { 492 | id: 64317, 493 | title: "The Great Gatsby", 494 | authors: [ 495 | { 496 | name: "Fitzgerald, F. Scott (Francis Scott)", 497 | birth_year: 1896, 498 | death_year: 1940, 499 | }, 500 | ], 501 | translators: [], 502 | subjects: [ 503 | "First loves -- Fiction", 504 | "Long Island (N.Y.) -- Fiction", 505 | "Married women -- Fiction", 506 | "Psychological fiction", 507 | "Rich people -- Fiction", 508 | ], 509 | bookshelves: [], 510 | languages: ["en"], 511 | copyright: false, 512 | media_type: "Text", 513 | formats: { 514 | "application/x-mobipocket-ebook": 515 | "https://www.gutenberg.org/ebooks/64317.kindle.images", 516 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/64317.rdf", 517 | "application/epub+zip": 518 | "https://www.gutenberg.org/ebooks/64317.epub.images", 519 | "image/jpeg": 520 | "https://www.gutenberg.org/cache/epub/64317/pg64317.cover.small.jpg", 521 | "text/plain": "https://www.gutenberg.org/ebooks/64317.txt.utf-8", 522 | "application/octet-stream": 523 | "https://www.gutenberg.org/files/64317/64317-h.zip", 524 | "application/zip": "https://www.gutenberg.org/files/64317/64317-0.zip", 525 | "text/html": "https://www.gutenberg.org/ebooks/64317.html.images", 526 | "text/plain; charset=us-ascii": 527 | "https://www.gutenberg.org/files/64317/64317-0.txt", 528 | }, 529 | download_count: 18414, 530 | }, 531 | { 532 | id: 844, 533 | title: 534 | "The Importance of Being Earnest: A Trivial Comedy for Serious People", 535 | authors: [ 536 | { 537 | name: "Wilde, Oscar", 538 | birth_year: 1854, 539 | death_year: 1900, 540 | }, 541 | ], 542 | translators: [], 543 | subjects: [ 544 | "Comedies", 545 | "England -- Drama", 546 | "Foundlings -- Drama", 547 | "Identity (Psychology) -- Drama", 548 | ], 549 | bookshelves: ["Plays"], 550 | languages: ["en"], 551 | copyright: false, 552 | media_type: "Text", 553 | formats: { 554 | "application/epub+zip": 555 | "https://www.gutenberg.org/ebooks/844.epub.images", 556 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/844.rdf", 557 | "application/x-mobipocket-ebook": 558 | "https://www.gutenberg.org/ebooks/844.kindle.images", 559 | "text/html": "https://www.gutenberg.org/files/844/844-h.zip", 560 | "text/plain; charset=utf-8": 561 | "https://www.gutenberg.org/files/844/844-0.zip", 562 | "image/jpeg": 563 | "https://www.gutenberg.org/cache/epub/844/pg844.cover.medium.jpg", 564 | }, 565 | download_count: 17878, 566 | }, 567 | { 568 | id: 98, 569 | title: "A Tale of Two Cities", 570 | authors: [ 571 | { 572 | name: "Dickens, Charles", 573 | birth_year: 1812, 574 | death_year: 1870, 575 | }, 576 | ], 577 | translators: [], 578 | subjects: [ 579 | "British -- France -- Paris -- Fiction", 580 | "Executions and executioners -- Fiction", 581 | "France -- History -- Revolution, 1789-1799 -- Fiction", 582 | "French -- England -- London -- Fiction", 583 | "Historical fiction", 584 | "London (England) -- History -- 18th century -- Fiction", 585 | "Lookalikes -- Fiction", 586 | "Paris (France) -- History -- 1789-1799 -- Fiction", 587 | "War stories", 588 | ], 589 | bookshelves: ["Historical Fiction"], 590 | languages: ["en"], 591 | copyright: false, 592 | media_type: "Text", 593 | formats: { 594 | "application/epub+zip": "https://www.gutenberg.org/ebooks/98.epub.images", 595 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/98.rdf", 596 | "application/x-mobipocket-ebook": 597 | "https://www.gutenberg.org/ebooks/98.kindle.images", 598 | "text/plain; charset=utf-8": 599 | "https://www.gutenberg.org/files/98/98-0.zip", 600 | "application/zip": "https://www.gutenberg.org/files/98/98-h.zip", 601 | "image/jpeg": 602 | "https://www.gutenberg.org/cache/epub/98/pg98.cover.medium.jpg", 603 | "text/html; charset=utf-8": 604 | "https://www.gutenberg.org/files/98/98-h/98-h.htm", 605 | "text/html": "https://www.gutenberg.org/ebooks/98.html.images", 606 | }, 607 | download_count: 17759, 608 | }, 609 | { 610 | id: 1952, 611 | title: "The Yellow Wallpaper", 612 | authors: [ 613 | { 614 | name: "Gilman, Charlotte Perkins", 615 | birth_year: 1860, 616 | death_year: 1935, 617 | }, 618 | ], 619 | translators: [], 620 | subjects: [ 621 | "Feminist fiction", 622 | "Married women -- Psychology -- Fiction", 623 | "Mentally ill women -- Fiction", 624 | "Psychological fiction", 625 | "Sex role -- Fiction", 626 | ], 627 | bookshelves: ["Gothic Fiction"], 628 | languages: ["en"], 629 | copyright: false, 630 | media_type: "Text", 631 | formats: { 632 | "image/jpeg": 633 | "https://www.gutenberg.org/cache/epub/1952/pg1952.cover.small.jpg", 634 | "application/x-mobipocket-ebook": 635 | "https://www.gutenberg.org/ebooks/1952.kindle.images", 636 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1952.rdf", 637 | "application/epub+zip": 638 | "https://www.gutenberg.org/ebooks/1952.epub.images", 639 | "application/zip": "https://www.gutenberg.org/files/1952/1952-h.zip", 640 | "text/html; charset=utf-8": 641 | "https://www.gutenberg.org/files/1952/1952-h/1952-h.htm", 642 | "text/html": "https://www.gutenberg.org/ebooks/1952.html.images", 643 | "text/plain; charset=utf-8": 644 | "https://www.gutenberg.org/files/1952/1952-0.txt", 645 | }, 646 | download_count: 17335, 647 | }, 648 | { 649 | id: 5200, 650 | title: "Metamorphosis", 651 | authors: [ 652 | { 653 | name: "Kafka, Franz", 654 | birth_year: 1883, 655 | death_year: 1924, 656 | }, 657 | ], 658 | translators: [ 659 | { 660 | name: "Wyllie, David (Translator)", 661 | birth_year: null, 662 | death_year: null, 663 | }, 664 | ], 665 | subjects: ["Metamorphosis -- Fiction", "Psychological fiction"], 666 | bookshelves: ["Horror"], 667 | languages: ["en"], 668 | copyright: true, 669 | media_type: "Text", 670 | formats: { 671 | "image/jpeg": 672 | "https://www.gutenberg.org/cache/epub/5200/pg5200.cover.medium.jpg", 673 | "application/epub+zip": 674 | "https://www.gutenberg.org/ebooks/5200.epub.images", 675 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/5200.rdf", 676 | "application/x-mobipocket-ebook": 677 | "https://www.gutenberg.org/ebooks/5200.kindle.images", 678 | "text/html; charset=iso-8859-1": 679 | "https://www.gutenberg.org/files/5200/5200-h/5200-h.htm", 680 | "text/plain; charset=utf-8": 681 | "https://www.gutenberg.org/files/5200/5200-0.zip", 682 | "application/zip": "https://www.gutenberg.org/files/5200/5200-h.zip", 683 | "text/html": "https://www.gutenberg.org/ebooks/5200.html.images", 684 | }, 685 | download_count: 16871, 686 | }, 687 | { 688 | id: 1260, 689 | title: "Jane Eyre: An Autobiography", 690 | authors: [ 691 | { 692 | name: "Brontë, Charlotte", 693 | birth_year: 1816, 694 | death_year: 1855, 695 | }, 696 | ], 697 | translators: [], 698 | subjects: [ 699 | "Bildungsromans", 700 | "Charity-schools -- Fiction", 701 | "Country homes -- Fiction", 702 | "England -- Fiction", 703 | "Fathers and daughters -- Fiction", 704 | "Governesses -- Fiction", 705 | "Love stories", 706 | "Married people -- Fiction", 707 | "Mentally ill women -- Fiction", 708 | "Orphans -- Fiction", 709 | "Young women -- Fiction", 710 | ], 711 | bookshelves: [], 712 | languages: ["en"], 713 | copyright: false, 714 | media_type: "Text", 715 | formats: { 716 | "application/x-mobipocket-ebook": 717 | "https://www.gutenberg.org/ebooks/1260.kindle.images", 718 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1260.rdf", 719 | "application/epub+zip": 720 | "https://www.gutenberg.org/ebooks/1260.epub.images", 721 | "application/zip": "https://www.gutenberg.org/files/1260/1260-h.zip", 722 | "text/plain; charset=utf-8": 723 | "https://www.gutenberg.org/files/1260/1260-0.txt", 724 | "text/html; charset=utf-8": 725 | "https://www.gutenberg.org/files/1260/1260-h/1260-h.htm", 726 | "image/jpeg": 727 | "https://www.gutenberg.org/cache/epub/1260/pg1260.cover.small.jpg", 728 | "text/html": "https://www.gutenberg.org/ebooks/1260.html.images", 729 | }, 730 | download_count: 16729, 731 | }, 732 | { 733 | id: 219, 734 | title: "Heart of Darkness", 735 | authors: [ 736 | { 737 | name: "Conrad, Joseph", 738 | birth_year: 1857, 739 | death_year: 1924, 740 | }, 741 | ], 742 | translators: [], 743 | subjects: [ 744 | "Africa -- Fiction", 745 | "Degeneration -- Fiction", 746 | "Europeans -- Africa -- Fiction", 747 | "Imperialism -- Fiction", 748 | "Psychological fiction", 749 | "Trading posts -- Fiction", 750 | ], 751 | bookshelves: ["Best Books Ever Listings", "Movie Books"], 752 | languages: ["en"], 753 | copyright: false, 754 | media_type: "Text", 755 | formats: { 756 | "application/epub+zip": 757 | "https://www.gutenberg.org/ebooks/219.epub.images", 758 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/219.rdf", 759 | "application/x-mobipocket-ebook": 760 | "https://www.gutenberg.org/ebooks/219.kindle.images", 761 | "text/html; charset=utf-8": 762 | "https://www.gutenberg.org/files/219/219-h/219-h.htm", 763 | "text/plain; charset=utf-8": 764 | "https://www.gutenberg.org/files/219/219-0.zip", 765 | "image/jpeg": 766 | "https://www.gutenberg.org/cache/epub/219/pg219.cover.small.jpg", 767 | "text/html": "https://www.gutenberg.org/ebooks/219.html.images", 768 | }, 769 | download_count: 16406, 770 | }, 771 | { 772 | id: 1232, 773 | title: "The Prince", 774 | authors: [ 775 | { 776 | name: "Machiavelli, Niccolò", 777 | birth_year: 1469, 778 | death_year: 1527, 779 | }, 780 | ], 781 | translators: [ 782 | { 783 | name: "Marriott, W. K. (William Kenaz)", 784 | birth_year: null, 785 | death_year: 1927, 786 | }, 787 | ], 788 | subjects: [ 789 | "Political ethics -- Early works to 1800", 790 | "Political science -- Philosophy -- Early works to 1800", 791 | "State, The -- Early works to 1800", 792 | ], 793 | bookshelves: [ 794 | "Banned Books from Anne Haight's list", 795 | "Harvard Classics", 796 | "Philosophy", 797 | "Politics", 798 | ], 799 | languages: ["en"], 800 | copyright: false, 801 | media_type: "Text", 802 | formats: { 803 | "application/x-mobipocket-ebook": 804 | "https://www.gutenberg.org/ebooks/1232.kindle.images", 805 | "application/epub+zip": 806 | "https://www.gutenberg.org/ebooks/1232.epub.images", 807 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1232.rdf", 808 | "image/jpeg": 809 | "https://www.gutenberg.org/cache/epub/1232/pg1232.cover.small.jpg", 810 | "text/html; charset=us-ascii": 811 | "https://www.gutenberg.org/files/1232/1232-h/1232-h.htm", 812 | "application/zip": "https://www.gutenberg.org/files/1232/1232-0.zip", 813 | "text/html": "https://www.gutenberg.org/ebooks/1232.html.images", 814 | "text/plain; charset=utf-8": 815 | "https://www.gutenberg.org/files/1232/1232-0.txt", 816 | }, 817 | download_count: 15620, 818 | }, 819 | { 820 | id: 205, 821 | title: "Walden, and On The Duty Of Civil Disobedience", 822 | authors: [ 823 | { 824 | name: "Thoreau, Henry David", 825 | birth_year: 1817, 826 | death_year: 1862, 827 | }, 828 | ], 829 | translators: [], 830 | subjects: [ 831 | "Authors, American -- 19th century -- Biography", 832 | "Civil disobedience", 833 | "Government, Resistance to", 834 | "Natural history -- Massachusetts -- Walden Woods", 835 | "Solitude", 836 | "Thoreau, Henry David, 1817-1862 -- Homes and haunts -- Massachusetts -- Walden Woods", 837 | "Walden Woods (Mass.) -- Social life and customs", 838 | "Wilderness areas -- Massachusetts -- Walden Woods", 839 | ], 840 | bookshelves: [], 841 | languages: ["en"], 842 | copyright: false, 843 | media_type: "Text", 844 | formats: { 845 | "image/jpeg": 846 | "https://www.gutenberg.org/cache/epub/205/pg205.cover.small.jpg", 847 | "application/epub+zip": 848 | "https://www.gutenberg.org/ebooks/205.epub.images", 849 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/205.rdf", 850 | "application/x-mobipocket-ebook": 851 | "https://www.gutenberg.org/ebooks/205.kindle.images", 852 | "text/html; charset=utf-8": 853 | "https://www.gutenberg.org/files/205/205-h.zip", 854 | "application/zip": "https://www.gutenberg.org/files/205/205-0.zip", 855 | "text/plain; charset=utf-8": 856 | "https://www.gutenberg.org/files/205/205-0.txt", 857 | "text/html": "https://www.gutenberg.org/ebooks/205.html.images", 858 | }, 859 | download_count: 14496, 860 | }, 861 | { 862 | id: 76, 863 | title: "Adventures of Huckleberry Finn", 864 | authors: [ 865 | { 866 | name: "Twain, Mark", 867 | birth_year: 1835, 868 | death_year: 1910, 869 | }, 870 | ], 871 | translators: [], 872 | subjects: [ 873 | "Adventure stories", 874 | "Bildungsromans", 875 | "Boys -- Fiction", 876 | "Finn, Huckleberry (Fictitious character) -- Fiction", 877 | "Fugitive slaves -- Fiction", 878 | "Humorous stories", 879 | "Male friendship -- Fiction", 880 | "Mississippi River -- Fiction", 881 | "Missouri -- Fiction", 882 | "Race relations -- Fiction", 883 | "Runaway children -- Fiction", 884 | ], 885 | bookshelves: [ 886 | "Banned Books List from the American Library Association", 887 | "Banned Books from Anne Haight's list", 888 | "Best Books Ever Listings", 889 | ], 890 | languages: ["en"], 891 | copyright: false, 892 | media_type: "Text", 893 | formats: { 894 | "application/epub+zip": "https://www.gutenberg.org/ebooks/76.epub.images", 895 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/76.rdf", 896 | "application/x-mobipocket-ebook": 897 | "https://www.gutenberg.org/ebooks/76.kindle.images", 898 | "text/html; charset=utf-8": 899 | "https://www.gutenberg.org/files/76/76-h/76-h.htm", 900 | "image/jpeg": 901 | "https://www.gutenberg.org/cache/epub/76/pg76.cover.small.jpg", 902 | "text/plain; charset=utf-8": 903 | "https://www.gutenberg.org/files/76/76-0.zip", 904 | "text/html": "https://www.gutenberg.org/ebooks/76.html.images", 905 | }, 906 | download_count: 14032, 907 | }, 908 | { 909 | id: 23, 910 | title: "Narrative of the Life of Frederick Douglass, an American Slave", 911 | authors: [ 912 | { 913 | name: "Douglass, Frederick", 914 | birth_year: 1818, 915 | death_year: 1895, 916 | }, 917 | ], 918 | translators: [], 919 | subjects: [ 920 | "Abolitionists -- United States -- Biography", 921 | "African American abolitionists -- Biography", 922 | "Douglass, Frederick, 1818-1895", 923 | "Slaves -- United States -- Biography", 924 | ], 925 | bookshelves: ["African American Writers", "Slavery"], 926 | languages: ["en"], 927 | copyright: false, 928 | media_type: "Text", 929 | formats: { 930 | "application/epub+zip": "https://www.gutenberg.org/ebooks/23.epub.images", 931 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/23.rdf", 932 | "application/x-mobipocket-ebook": 933 | "https://www.gutenberg.org/ebooks/23.kindle.images", 934 | "image/jpeg": 935 | "https://www.gutenberg.org/cache/epub/23/pg23.cover.medium.jpg", 936 | "text/html; charset=us-ascii": 937 | "https://www.gutenberg.org/files/23/23-h.zip", 938 | "text/plain; charset=utf-8": 939 | "https://www.gutenberg.org/files/23/23-0.txt", 940 | "text/html": "https://www.gutenberg.org/ebooks/23.html.images", 941 | }, 942 | download_count: 13679, 943 | }, 944 | { 945 | id: 2500, 946 | title: "Siddhartha", 947 | authors: [ 948 | { 949 | name: "Hesse, Hermann", 950 | birth_year: 1877, 951 | death_year: 1962, 952 | }, 953 | ], 954 | translators: [], 955 | subjects: [ 956 | "Brahmans -- Fiction", 957 | "Buddhism -- Fiction", 958 | "Buddhist philosophy -- Fiction", 959 | "Gautama Buddha -- Fiction", 960 | "India -- Religion -- Fiction", 961 | "India -- Social life and customs -- Fiction", 962 | "Self-realization -- Fiction", 963 | "Spiritual life -- Fiction", 964 | ], 965 | bookshelves: [], 966 | languages: ["en"], 967 | copyright: false, 968 | media_type: "Text", 969 | formats: { 970 | "text/plain; charset=utf-8": 971 | "https://www.gutenberg.org/files/2500/2500-0.txt", 972 | "application/x-mobipocket-ebook": 973 | "https://www.gutenberg.org/ebooks/2500.kindle.images", 974 | "application/epub+zip": 975 | "https://www.gutenberg.org/ebooks/2500.epub.images", 976 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/2500.rdf", 977 | "text/html; charset=iso-8859-1": 978 | "https://www.gutenberg.org/files/2500/2500-h/2500-h.htm", 979 | "image/jpeg": 980 | "https://www.gutenberg.org/cache/epub/2500/pg2500.cover.small.jpg", 981 | "text/html": "https://www.gutenberg.org/ebooks/2500.html.images", 982 | }, 983 | download_count: 13046, 984 | }, 985 | { 986 | id: 2554, 987 | title: "Crime and Punishment", 988 | authors: [ 989 | { 990 | name: "Dostoyevsky, Fyodor", 991 | birth_year: 1821, 992 | death_year: 1881, 993 | }, 994 | ], 995 | translators: [ 996 | { 997 | name: "Garnett, Constance", 998 | birth_year: 1861, 999 | death_year: 1946, 1000 | }, 1001 | ], 1002 | subjects: [ 1003 | "Crime -- Psychological aspects -- Fiction", 1004 | "Detective and mystery stories", 1005 | "Murder -- Fiction", 1006 | "Psychological fiction", 1007 | "Saint Petersburg (Russia) -- Fiction", 1008 | ], 1009 | bookshelves: [ 1010 | "Best Books Ever Listings", 1011 | "Crime Fiction", 1012 | "Harvard Classics", 1013 | ], 1014 | languages: ["en"], 1015 | copyright: false, 1016 | media_type: "Text", 1017 | formats: { 1018 | "application/x-mobipocket-ebook": 1019 | "https://www.gutenberg.org/ebooks/2554.kindle.images", 1020 | "application/epub+zip": 1021 | "https://www.gutenberg.org/ebooks/2554.epub.images", 1022 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/2554.rdf", 1023 | "text/html; charset=utf-8": 1024 | "https://www.gutenberg.org/files/2554/2554-h.zip", 1025 | "text/html": "https://www.gutenberg.org/ebooks/2554.html.images", 1026 | "image/jpeg": 1027 | "https://www.gutenberg.org/cache/epub/2554/pg2554.cover.medium.jpg", 1028 | "text/plain; charset=utf-8": 1029 | "https://www.gutenberg.org/files/2554/2554-0.zip", 1030 | }, 1031 | download_count: 12560, 1032 | }, 1033 | { 1034 | id: 4980, 1035 | title: "Old Granny Fox", 1036 | authors: [ 1037 | { 1038 | name: "Burgess, Thornton W. (Thornton Waldo)", 1039 | birth_year: 1874, 1040 | death_year: 1965, 1041 | }, 1042 | ], 1043 | translators: [], 1044 | subjects: ["Animals -- Juvenile fiction", "Foxes -- Juvenile fiction"], 1045 | bookshelves: ["Children's Literature"], 1046 | languages: ["en"], 1047 | copyright: false, 1048 | media_type: "Text", 1049 | formats: { 1050 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/4980.rdf", 1051 | "application/epub+zip": 1052 | "https://www.gutenberg.org/ebooks/4980.epub.images", 1053 | "application/x-mobipocket-ebook": 1054 | "https://www.gutenberg.org/ebooks/4980.kindle.images", 1055 | "application/octet-stream": 1056 | "https://www.gutenberg.org/files/4980/4980-0.zip", 1057 | "application/zip": "https://www.gutenberg.org/files/4980/4980-h.zip", 1058 | "image/jpeg": 1059 | "https://www.gutenberg.org/cache/epub/4980/pg4980.cover.medium.jpg", 1060 | "text/html": "https://www.gutenberg.org/ebooks/4980.html.images", 1061 | "text/plain; charset=us-ascii": 1062 | "https://www.gutenberg.org/files/4980/4980-0.txt", 1063 | "text/plain": "https://www.gutenberg.org/ebooks/4980.txt.utf-8", 1064 | }, 1065 | download_count: 12380, 1066 | }, 1067 | { 1068 | id: 2591, 1069 | title: "Grimms' Fairy Tales", 1070 | authors: [ 1071 | { 1072 | name: "Grimm, Wilhelm", 1073 | birth_year: 1786, 1074 | death_year: 1859, 1075 | }, 1076 | { 1077 | name: "Grimm, Jacob", 1078 | birth_year: 1785, 1079 | death_year: 1863, 1080 | }, 1081 | ], 1082 | translators: [], 1083 | subjects: ["Fairy tales -- Germany"], 1084 | bookshelves: [], 1085 | languages: ["en"], 1086 | copyright: false, 1087 | media_type: "Text", 1088 | formats: { 1089 | "application/x-mobipocket-ebook": 1090 | "https://www.gutenberg.org/ebooks/2591.kindle.images", 1091 | "application/epub+zip": 1092 | "https://www.gutenberg.org/ebooks/2591.epub.images", 1093 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/2591.rdf", 1094 | "application/zip": "https://www.gutenberg.org/files/2591/2591-h.zip", 1095 | "text/html; charset=utf-8": 1096 | "https://www.gutenberg.org/files/2591/2591-h/2591-h.htm", 1097 | "text/plain; charset=utf-8": 1098 | "https://www.gutenberg.org/files/2591/2591-0.txt", 1099 | "image/jpeg": 1100 | "https://www.gutenberg.org/cache/epub/2591/pg2591.cover.small.jpg", 1101 | "text/html": "https://www.gutenberg.org/ebooks/2591.html.images", 1102 | }, 1103 | download_count: 11952, 1104 | }, 1105 | { 1106 | id: 16328, 1107 | title: "Beowulf: An Anglo-Saxon Epic Poem", 1108 | authors: [], 1109 | translators: [ 1110 | { 1111 | name: "Hall, J. Lesslie (John Lesslie)", 1112 | birth_year: 1856, 1113 | death_year: 1928, 1114 | }, 1115 | ], 1116 | subjects: [ 1117 | "Dragons -- Poetry", 1118 | "Epic poetry, English (Old)", 1119 | "Monsters -- Poetry", 1120 | ], 1121 | bookshelves: ["Poetry"], 1122 | languages: ["en"], 1123 | copyright: false, 1124 | media_type: "Text", 1125 | formats: { 1126 | "text/html": "https://www.gutenberg.org/ebooks/16328.html.images", 1127 | "application/x-mobipocket-ebook": 1128 | "https://www.gutenberg.org/ebooks/16328.kindle.images", 1129 | "application/epub+zip": 1130 | "https://www.gutenberg.org/ebooks/16328.epub.images", 1131 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/16328.rdf", 1132 | "text/html; charset=iso-8859-1": 1133 | "https://www.gutenberg.org/files/16328/16328-h.zip", 1134 | "image/jpeg": 1135 | "https://www.gutenberg.org/cache/epub/16328/pg16328.cover.small.jpg", 1136 | "application/zip": "https://www.gutenberg.org/files/16328/16328-0.zip", 1137 | "text/plain; charset=utf-8": 1138 | "https://www.gutenberg.org/files/16328/16328-0.txt", 1139 | }, 1140 | download_count: 11853, 1141 | }, 1142 | { 1143 | id: 1727, 1144 | title: 1145 | "The Odyssey: Rendered into English prose for the use of those who cannot read the original", 1146 | authors: [ 1147 | { 1148 | name: "Homer", 1149 | birth_year: -750, 1150 | death_year: -650, 1151 | }, 1152 | ], 1153 | translators: [ 1154 | { 1155 | name: "Butler, Samuel", 1156 | birth_year: 1835, 1157 | death_year: 1902, 1158 | }, 1159 | ], 1160 | subjects: [ 1161 | "Epic poetry, Greek -- Translations into English", 1162 | "Homer -- Translations into English", 1163 | "Odysseus, King of Ithaca (Mythological character)", 1164 | ], 1165 | bookshelves: ["Classical Antiquity", "Harvard Classics"], 1166 | languages: ["en"], 1167 | copyright: false, 1168 | media_type: "Text", 1169 | formats: { 1170 | "image/jpeg": 1171 | "https://www.gutenberg.org/cache/epub/1727/pg1727.cover.medium.jpg", 1172 | "application/x-mobipocket-ebook": 1173 | "https://www.gutenberg.org/ebooks/1727.kindle.images", 1174 | "application/epub+zip": 1175 | "https://www.gutenberg.org/ebooks/1727.epub.images", 1176 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1727.rdf", 1177 | "text/plain; charset=us-ascii": 1178 | "https://www.gutenberg.org/files/1727/1727-0.txt", 1179 | "application/octet-stream": 1180 | "https://www.gutenberg.org/files/1727/1727-h.zip", 1181 | "text/html": "https://www.gutenberg.org/files/1727/1727-h/1727-h.htm", 1182 | "text/plain": "https://www.gutenberg.org/ebooks/1727.txt.utf-8", 1183 | }, 1184 | download_count: 11464, 1185 | }, 1186 | { 1187 | id: 1250, 1188 | title: "Anthem", 1189 | authors: [ 1190 | { 1191 | name: "Rand, Ayn", 1192 | birth_year: 1905, 1193 | death_year: 1982, 1194 | }, 1195 | ], 1196 | translators: [], 1197 | subjects: [ 1198 | "Individuality -- Fiction", 1199 | "Love stories", 1200 | "Men -- Psychology -- Fiction", 1201 | "Psychological fiction", 1202 | "Science fiction", 1203 | "Time travel -- Fiction", 1204 | ], 1205 | bookshelves: ["Science Fiction", "Science Fiction by Women"], 1206 | languages: ["en"], 1207 | copyright: false, 1208 | media_type: "Text", 1209 | formats: { 1210 | "application/x-mobipocket-ebook": 1211 | "https://www.gutenberg.org/ebooks/1250.kindle.images", 1212 | "application/epub+zip": 1213 | "https://www.gutenberg.org/ebooks/1250.epub.images", 1214 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1250.rdf", 1215 | "application/zip": "https://www.gutenberg.org/files/1250/1250-0.zip", 1216 | "text/plain; charset=utf-8": 1217 | "https://www.gutenberg.org/files/1250/1250-0.txt", 1218 | "image/jpeg": 1219 | "https://www.gutenberg.org/cache/epub/1250/pg1250.cover.medium.jpg", 1220 | "text/html; charset=us-ascii": 1221 | "https://www.gutenberg.org/files/1250/1250-h/1250-h.htm", 1222 | "text/html": "https://www.gutenberg.org/ebooks/1250.html.images", 1223 | }, 1224 | download_count: 11385, 1225 | }, 1226 | { 1227 | id: 160, 1228 | title: "The Awakening, and Selected Short Stories", 1229 | authors: [ 1230 | { 1231 | name: "Chopin, Kate", 1232 | birth_year: 1850, 1233 | death_year: 1904, 1234 | }, 1235 | ], 1236 | translators: [], 1237 | subjects: [ 1238 | "Adultery -- Fiction", 1239 | "Louisiana -- Social life and customs -- Fiction", 1240 | "New Orleans (La.) -- Fiction", 1241 | "Self-actualization (Psychology) -- Fiction", 1242 | "Women -- Louisiana -- New Orleans -- Social conditions -- Fiction", 1243 | ], 1244 | bookshelves: [ 1245 | "Banned Books List from the American Library Association", 1246 | "Banned Books from Anne Haight's list", 1247 | ], 1248 | languages: ["en"], 1249 | copyright: false, 1250 | media_type: "Text", 1251 | formats: { 1252 | "application/epub+zip": 1253 | "https://www.gutenberg.org/ebooks/160.epub.images", 1254 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/160.rdf", 1255 | "text/plain; charset=utf-8": 1256 | "https://www.gutenberg.org/files/160/160-0.txt", 1257 | "application/x-mobipocket-ebook": 1258 | "https://www.gutenberg.org/ebooks/160.kindle.images", 1259 | "image/jpeg": 1260 | "https://www.gutenberg.org/cache/epub/160/pg160.cover.small.jpg", 1261 | "text/html; charset=utf-8": 1262 | "https://www.gutenberg.org/files/160/160-h.zip", 1263 | "text/html": "https://www.gutenberg.org/ebooks/160.html.images", 1264 | "application/zip": "https://www.gutenberg.org/files/160/160-0.zip", 1265 | }, 1266 | download_count: 11343, 1267 | }, 1268 | { 1269 | id: 1400, 1270 | title: "Great Expectations", 1271 | authors: [ 1272 | { 1273 | name: "Dickens, Charles", 1274 | birth_year: 1812, 1275 | death_year: 1870, 1276 | }, 1277 | ], 1278 | translators: [], 1279 | subjects: [ 1280 | "Benefactors -- Fiction", 1281 | "Bildungsromans", 1282 | "England -- Fiction", 1283 | "Ex-convicts -- Fiction", 1284 | "Man-woman relationships -- Fiction", 1285 | "Orphans -- Fiction", 1286 | "Revenge -- Fiction", 1287 | "Young men -- Fiction", 1288 | ], 1289 | bookshelves: ["Best Books Ever Listings"], 1290 | languages: ["en"], 1291 | copyright: false, 1292 | media_type: "Text", 1293 | formats: { 1294 | "text/plain; charset=utf-8": 1295 | "https://www.gutenberg.org/files/1400/1400-0.txt", 1296 | "application/x-mobipocket-ebook": 1297 | "https://www.gutenberg.org/ebooks/1400.kindle.images", 1298 | "application/epub+zip": 1299 | "https://www.gutenberg.org/ebooks/1400.epub.images", 1300 | "application/rdf+xml": "https://www.gutenberg.org/ebooks/1400.rdf", 1301 | "application/zip": "https://www.gutenberg.org/files/1400/1400-h.zip", 1302 | "text/html; charset=utf-8": 1303 | "https://www.gutenberg.org/files/1400/1400-h/1400-h.htm", 1304 | "text/html": "https://www.gutenberg.org/ebooks/1400.html.images", 1305 | "image/jpeg": 1306 | "https://www.gutenberg.org/cache/epub/1400/pg1400.cover.small.jpg", 1307 | }, 1308 | download_count: 11235, 1309 | }, 1310 | ]; 1311 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/src/index.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as ReactDOM from "react-dom"; 3 | import { RelayEnvironmentProvider } from "react-relay/hooks"; 4 | import { Environment, Network, RecordSource, Store } from "relay-runtime"; 5 | import { App } from "./App"; 6 | 7 | async function fetchRelay(params, variables) { 8 | console.log( 9 | `Fetching query ${params.name} with ${JSON.stringify(variables)}` 10 | ); 11 | 12 | const response = await fetch("http://localhost:4001/", { 13 | method: "POST", 14 | headers: { 15 | "Content-Type": "application/json", 16 | }, 17 | body: JSON.stringify({ 18 | query: params.text, 19 | variables, 20 | }), 21 | }); 22 | 23 | return await response.json(); 24 | } 25 | 26 | const environment = new Environment({ 27 | network: Network.create(fetchRelay), 28 | store: new Store(new RecordSource()), 29 | }); 30 | 31 | ReactDOM.render( 32 | 33 | 34 | 35 | 36 | , 37 | document.getElementById("root") 38 | ); 39 | -------------------------------------------------------------------------------- /examples/books-browser-javascript/src/server.js: -------------------------------------------------------------------------------- 1 | const { ApolloServer, gql } = require("apollo-server"); 2 | const data = require("./data"); 3 | 4 | const typeDefs = gql` 5 | type Book { 6 | id: String! 7 | title: String 8 | } 9 | 10 | type Query { 11 | books(first: Int): [Book] 12 | } 13 | `; 14 | 15 | function toGlobalId(type, id) { 16 | return Buffer.from(`${type}:${id}`).toString("base64"); 17 | } 18 | 19 | const resolvers = { 20 | Query: { 21 | books: () => 22 | data.map((book) => ({ ...book, id: toGlobalId("book", book.id) })), 23 | }, 24 | }; 25 | 26 | const port = process.env.PORT || "4001"; 27 | const server = new ApolloServer({ 28 | typeDefs, 29 | resolvers, 30 | }); 31 | 32 | server.listen({ port }).then(({ url }) => { 33 | console.log(`Mock server running at ${url}`); 34 | }); 35 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | roots: ["/src"], 3 | testMatch: ["**/__tests__/**/*.+(ts)", "**/?(*.)+(spec|test).+(ts)"], 4 | transform: { 5 | "^.+\\.(ts)$": "ts-jest", 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-plugin-relay", 3 | "version": "0.1.0", 4 | "description": "An esbuild plugin to transform tagged GraphQL template literals for Relay", 5 | "type": "module", 6 | "main": "./lib/index.cjs", 7 | "types": "./lib/index.d.ts", 8 | "exports": { 9 | "import": "./lib/index.mjs", 10 | "require": "./lib/index.cjs" 11 | }, 12 | "keywords": [ 13 | "graphql", 14 | "relay", 15 | "esbuild", 16 | "esbuild-plugin" 17 | ], 18 | "files": [ 19 | "lib/*" 20 | ], 21 | "scripts": { 22 | "build": "./scripts/build.cjs", 23 | "build:watch": "./scripts/build.cjs -w", 24 | "test": "jest" 25 | }, 26 | "author": "smartvokat GmbH", 27 | "license": "MIT", 28 | "peerDependencies": { 29 | "graphql": "^15.0.0" 30 | }, 31 | "devDependencies": { 32 | "@types/jest": "^29.4.0", 33 | "esbuild": "^0.17.5", 34 | "estrella": "^1.4.1", 35 | "graphql": "^15.5.1", 36 | "jest": "^29.4.1", 37 | "publint": "^0.1.9", 38 | "relay-config": "^12.0.1", 39 | "ts-jest": "^29.0.5", 40 | "typescript": "^4.9.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /scripts/build.cjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const path = require("path"); 3 | const { build, ts, tsconfig, dirname, glob, log } = require("estrella"); 4 | 5 | const buildOptions = { 6 | cwd: path.join(__dirname, ".."), 7 | bundle: true, 8 | minify: process.env.NODE_ENV === "production", 9 | platform: "node", 10 | external: ["graphql", "relay-config"], 11 | target: "node16", 12 | }; 13 | 14 | build({ 15 | ...buildOptions, 16 | entry: "src/index.ts", 17 | outfile: "lib/index.mjs", 18 | format: "esm", 19 | onEnd(config) { 20 | const dtsFilesOutdir = dirname(config.outfile); 21 | generateTypeDefs(tsconfig(config), config.entry, dtsFilesOutdir); 22 | }, 23 | }); 24 | 25 | build({ 26 | ...buildOptions, 27 | entry: "src/index.cjs.ts", 28 | outfile: "lib/index.cjs", 29 | format: "cjs", 30 | }); 31 | 32 | function generateTypeDefs(tsconfig, entryfiles, outdir) { 33 | const filenames = Array.from( 34 | new Set( 35 | (Array.isArray(entryfiles) ? entryfiles : [entryfiles]).concat( 36 | tsconfig.include || [] 37 | ) 38 | ) 39 | ).filter(Boolean); 40 | 41 | log.info("Generating type declaration files for", filenames.join(", ")); 42 | 43 | const compilerOptions = { 44 | ...tsconfig.compilerOptions, 45 | moduleResolution: undefined, 46 | declaration: true, 47 | outDir: outdir, 48 | }; 49 | 50 | const program = ts.ts.createProgram(filenames, compilerOptions); 51 | program.emit(undefined, undefined, undefined, true); 52 | 53 | log.info("Wrote", glob(outdir + "/*.d.ts").join(", ")); 54 | } 55 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/compile.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`handles fragments 1`] = ` 4 | "import graphql__0bb6b7b29bc3e910921551c4ff5b6757 from "./__generated__/TestFrag.graphql"; 5 | graphql__0bb6b7b29bc3e910921551c4ff5b6757" 6 | `; 7 | 8 | exports[`handles mutations 1`] = ` 9 | "import graphql__a16dfb32ff638dca8f34b6a63572cfeb from "../../esbuild-plugin-relay/__generated__/CreateUser.graphql"; 10 | graphql__a16dfb32ff638dca8f34b6a63572cfeb" 11 | `; 12 | 13 | exports[`handles queries 1`] = ` 14 | "import graphql__82961b21f6188a8e1bfe1c531398338d from "../../../../src/__generated__/SomeQuery.graphql"; 15 | graphql__82961b21f6188a8e1bfe1c531398338d" 16 | `; 17 | 18 | exports[`handles subscriptions 1`] = ` 19 | "import graphql__6dcdb2bafeca3c8b27e539bc11e725ae from "../../esbuild-plugin-relay/__generated__/FeedbackLikeSubscription.graphql"; 20 | graphql__6dcdb2bafeca3c8b27e539bc11e725ae" 21 | `; 22 | 23 | exports[`with CommonJS modules handles inline tagged template literals 1`] = ` 24 | " 25 | function SomeComponent() { 26 | const _graphql = {}; 27 | const graphql = {}; 28 | const graphql_ = "graphql"; 29 | 30 | return ( 31 | 32 | 37 | 38 | ); 39 | }" 40 | `; 41 | 42 | exports[`with CommonJS modules handles multiple tagged template literals 1`] = ` 43 | " 44 | /** 45 | * Copyright (c) Example Inc. 46 | * 47 | * This source code is licensed under the MIT license found in the 48 | * LICENSE file in the root directory of this source tree. 49 | */ 50 | 51 | 'use strict'; 52 | 53 | import { graphql } from "react-relay/hooks"; 54 | 55 | const someQuery = require("../../__graphql__/SomeQuery.graphql"); 56 | 57 | const anotherQuery = require("../../__graphql__/AnotherQuery.graphql");" 58 | `; 59 | 60 | exports[`with ESM modules handles fragments 1`] = ` 61 | "import graphql__0bb6b7b29bc3e910921551c4ff5b6757 from "./__generated__/TestFrag.graphql"; 62 | graphql__0bb6b7b29bc3e910921551c4ff5b6757" 63 | `; 64 | 65 | exports[`with ESM modules handles inline tagged template literals 1`] = ` 66 | "import graphql__a20897165ec52e5fd688ea09e854dfcc from "../../../assets/graphql/AnotherQuery.graphql.js"; 67 | 68 | function SomeComponent() { 69 | const _graphql = {}; 70 | const graphql = {}; 71 | const graphql_ = "graphql"; 72 | 73 | return ( 74 | 75 | 80 | 81 | ); 82 | }" 83 | `; 84 | 85 | exports[`with ESM modules handles multiple tagged template literals 1`] = ` 86 | "import graphql__82961b21f6188a8e1bfe1c531398338d from "../../__graphql__/SomeQuery.graphql"; 87 | import graphql__a20897165ec52e5fd688ea09e854dfcc from "../../__graphql__/AnotherQuery.graphql"; 88 | 89 | /** 90 | * Copyright (c) Example Inc. 91 | * 92 | * This source code is licensed under the MIT license found in the 93 | * LICENSE file in the root directory of this source tree. 94 | */ 95 | 96 | 'use strict'; 97 | 98 | import { graphql } from "react-relay/hooks"; 99 | 100 | const someQuery = graphql__82961b21f6188a8e1bfe1c531398338d; 101 | 102 | const anotherQuery = graphql__a20897165ec52e5fd688ea09e854dfcc;" 103 | `; 104 | 105 | exports[`with enabled development mode compares the hash in a CJS module 1`] = ` 106 | " 107 | /** 108 | * Copyright (c) Example Inc. 109 | * 110 | * This source code is licensed under the MIT license found in the 111 | * LICENSE file in the root directory of this source tree. 112 | */ 113 | 114 | 'use strict'; 115 | 116 | const {graphql} = require('relay-runtime'); 117 | 118 | const fragment = graphql__853140a093e3db9b64cb4111ee2de8dd !== void 0 ? graphql__853140a093e3db9b64cb4111ee2de8dd : (graphql__853140a093e3db9b64cb4111ee2de8dd = require("./__generated__/AFragment.graphql"), graphql__853140a093e3db9b64cb4111ee2de8dd.hash && graphql__853140a093e3db9b64cb4111ee2de8dd.hash !== "853140a093e3db9b64cb4111ee2de8dd" && console.error("The definition of 'AFragment' appears to have changed. Run \`relay-compiler\` to update the generated files to receive the expected data."), graphql__853140a093e3db9b64cb4111ee2de8dd); 119 | " 120 | `; 121 | 122 | exports[`with enabled development mode compares the hash in a CJS module with a custom condition 1`] = ` 123 | " 124 | fetchQuery( 125 | environment, 126 | graphql__20457954866480ef5ba77c6d8498f295 !== void 0 ? graphql__20457954866480ef5ba77c6d8498f295 : (graphql__20457954866480ef5ba77c6d8498f295 = require("../src/__generated__/AppQuery.graphql"), IS_DEV == true && graphql__20457954866480ef5ba77c6d8498f295.hash && graphql__20457954866480ef5ba77c6d8498f295.hash !== "20457954866480ef5ba77c6d8498f295" && console.error("The definition of 'AppQuery' appears to have changed. Run \`relay-compiler\` to update the generated files to receive the expected data."), graphql__20457954866480ef5ba77c6d8498f295), 127 | {id: 4}, 128 | ) 129 | .subscribe({ 130 | start: console.log, 131 | complete: console.log, 132 | error: console.log, 133 | next: console.log 134 | }); 135 | " 136 | `; 137 | 138 | exports[`with enabled development mode compares the hash in an ESM module 1`] = ` 139 | "import graphql__853140a093e3db9b64cb4111ee2de8dd from "./__generated__/AFragment.graphql"; 140 | 141 | /** 142 | * Copyright (c) Example Inc. 143 | * 144 | * This source code is licensed under the MIT license found in the 145 | * LICENSE file in the root directory of this source tree. 146 | */ 147 | 148 | 'use strict'; 149 | 150 | const {graphql} = require('relay-runtime'); 151 | 152 | const fragment = (graphql__853140a093e3db9b64cb4111ee2de8dd.hash && graphql__853140a093e3db9b64cb4111ee2de8dd.hash !== "853140a093e3db9b64cb4111ee2de8dd" && console.error("The definition of 'AFragment' appears to have changed. Run \`relay-compiler\` to update the generated files to receive the expected data."), graphql__853140a093e3db9b64cb4111ee2de8dd); 153 | " 154 | `; 155 | 156 | exports[`with enabled development mode compares the hash in an ESM module with a custom condition 1`] = ` 157 | "import graphql__f7192c5a0ab88781f703d276e7bd22a0 from "../../__generated__/Query.graphql"; 158 | 159 | function Component() { 160 | useLazyQuery((process.env.NODE_ENV !== "production" && graphql__f7192c5a0ab88781f703d276e7bd22a0.hash && graphql__f7192c5a0ab88781f703d276e7bd22a0.hash !== "f7192c5a0ab88781f703d276e7bd22a0" && console.error("The definition of 'Query' appears to have changed. Run \`relay-compiler\` to update the generated files to receive the expected data."), graphql__f7192c5a0ab88781f703d276e7bd22a0)); 161 | } 162 | " 163 | `; 164 | -------------------------------------------------------------------------------- /src/__tests__/compile.test.ts: -------------------------------------------------------------------------------- 1 | import { compile } from "../compile"; 2 | 3 | test("handles fragments", () => { 4 | const code = compile( 5 | "./src/test.ts", 6 | "graphql`fragment TestFrag on Node { id }`", 7 | { artifactDirectory: "./src/__generated__" } 8 | ); 9 | 10 | expect(code).toMatchSnapshot(); 11 | }); 12 | 13 | test("handles queries", () => { 14 | const code = compile( 15 | "./test/some/nested/folder/file.ts", 16 | "graphql`query SomeQuery($id: ID!) { node(id: $id) { id }}`", 17 | { artifactDirectory: "./src/__generated__" } 18 | ); 19 | 20 | expect(code).toMatchSnapshot(); 21 | }); 22 | 23 | test("handles mutations", () => { 24 | const code = compile( 25 | "../another/folder/file.js", 26 | "graphql`mutation CreateUser($name: String!) { createUser(name: $name) { id }}`", 27 | { artifactDirectory: "./__generated__" } 28 | ); 29 | 30 | expect(code).toMatchSnapshot(); 31 | }); 32 | 33 | test("handles subscriptions", () => { 34 | const code = compile( 35 | "../another/folder/file.js", 36 | "graphql`subscription FeedbackLikeSubscription { likes }`", 37 | { artifactDirectory: "./__generated__" } 38 | ); 39 | 40 | expect(code).toMatchSnapshot(); 41 | }); 42 | 43 | test("fail on nameless operation", () => { 44 | expect(() => { 45 | compile("./src/test.ts", "graphql`{ node { id }}`", { 46 | artifactDirectory: "./src/__generated__", 47 | }); 48 | }).toThrowError("GraphQL operations and fragments must contain names"); 49 | }); 50 | 51 | describe("with ESM modules", () => { 52 | test("handles fragments", () => { 53 | const code = compile( 54 | "./src/test.ts", 55 | "graphql`fragment TestFrag on Node { id }`", 56 | { artifactDirectory: "./src/__generated__" } 57 | ); 58 | 59 | expect(code).toMatchSnapshot(); 60 | }); 61 | 62 | test("handles multiple tagged template literals", () => { 63 | const code = ` 64 | /** 65 | * Copyright (c) Example Inc. 66 | * 67 | * This source code is licensed under the MIT license found in the 68 | * LICENSE file in the root directory of this source tree. 69 | */ 70 | 71 | 'use strict'; 72 | 73 | import { graphql } from "react-relay/hooks"; 74 | 75 | const someQuery = graphql\` 76 | query SomeQuery($id: ID!) { 77 | node(id: $id) { id } 78 | } 79 | \`; 80 | 81 | const anotherQuery = graphql\` 82 | query AnotherQuery($id: ID!) { 83 | node(id: $id) { id } 84 | } 85 | \`;`; 86 | 87 | expect( 88 | compile("./src/some/file.js", code, { 89 | artifactDirectory: "__graphql__", 90 | }) 91 | ).toMatchSnapshot(); 92 | }); 93 | 94 | test("handles inline tagged template literals", () => { 95 | const code = ` 96 | function SomeComponent() { 97 | const _graphql = {}; 98 | const graphql = {}; 99 | const graphql_ = "graphql"; 100 | 101 | return ( 102 | 103 | 112 | 113 | ); 114 | }`; 115 | 116 | expect( 117 | compile("./src/components/SomeComponent.js", code, { 118 | suffix: ".js", 119 | artifactDirectory: "../assets/graphql", 120 | }) 121 | ).toMatchSnapshot(); 122 | }); 123 | }); 124 | 125 | describe("with CommonJS modules", () => { 126 | test("handles multiple tagged template literals", () => { 127 | const code = ` 128 | /** 129 | * Copyright (c) Example Inc. 130 | * 131 | * This source code is licensed under the MIT license found in the 132 | * LICENSE file in the root directory of this source tree. 133 | */ 134 | 135 | 'use strict'; 136 | 137 | import { graphql } from "react-relay/hooks"; 138 | 139 | const someQuery = graphql\` 140 | query SomeQuery($id: ID!) { 141 | node(id: $id) { id } 142 | } 143 | \`; 144 | 145 | const anotherQuery = graphql\` 146 | query AnotherQuery($id: ID!) { 147 | node(id: $id) { id } 148 | } 149 | \`;`; 150 | 151 | expect( 152 | compile("./src/some/file.js", code, { 153 | module: "cjs", 154 | artifactDirectory: "__graphql__", 155 | }) 156 | ).toMatchSnapshot(); 157 | }); 158 | 159 | test("handles inline tagged template literals", () => { 160 | const code = ` 161 | function SomeComponent() { 162 | const _graphql = {}; 163 | const graphql = {}; 164 | const graphql_ = "graphql"; 165 | 166 | return ( 167 | 168 | 177 | 178 | ); 179 | }`; 180 | 181 | expect( 182 | compile("./src/components/SomeComponent.js", code, { 183 | module: "cjs", 184 | suffix: ".js", 185 | artifactDirectory: "../assets/graphql", 186 | }) 187 | ).toMatchSnapshot(); 188 | }); 189 | }); 190 | 191 | describe("with enabled development mode", () => { 192 | test("compares the hash in an ESM module", () => { 193 | const code = compile( 194 | "src/file.js", 195 | ` 196 | /** 197 | * Copyright (c) Example Inc. 198 | * 199 | * This source code is licensed under the MIT license found in the 200 | * LICENSE file in the root directory of this source tree. 201 | */ 202 | 203 | 'use strict'; 204 | 205 | const {graphql} = require('relay-runtime'); 206 | 207 | const fragment = graphql\` 208 | fragment AFragment on User { 209 | id 210 | } 211 | \`; 212 | `, 213 | { artifactDirectory: "./src/__generated__", devMode: true } 214 | ); 215 | 216 | expect(code).toMatchSnapshot(); 217 | }); 218 | 219 | test("compares the hash in an ESM module with a custom condition", () => { 220 | const code = compile( 221 | "src/nested/dir/component.js", 222 | ` 223 | function Component() { 224 | useLazyQuery(graphql\` 225 | query Query { 226 | id 227 | } 228 | \`); 229 | } 230 | `, 231 | { 232 | artifactDirectory: "./src/__generated__", 233 | devMode: true, 234 | condition: 'process.env.NODE_ENV !== "production"', 235 | } 236 | ); 237 | 238 | expect(code).toMatchSnapshot(); 239 | }); 240 | 241 | test("compares the hash in a CJS module", () => { 242 | const code = compile( 243 | "src/file.js", 244 | ` 245 | /** 246 | * Copyright (c) Example Inc. 247 | * 248 | * This source code is licensed under the MIT license found in the 249 | * LICENSE file in the root directory of this source tree. 250 | */ 251 | 252 | 'use strict'; 253 | 254 | const {graphql} = require('relay-runtime'); 255 | 256 | const fragment = graphql\` 257 | fragment AFragment on User { 258 | id 259 | } 260 | \`; 261 | `, 262 | { artifactDirectory: "./src/__generated__", devMode: true, module: "cjs" } 263 | ); 264 | 265 | expect(code).toMatchSnapshot(); 266 | }); 267 | 268 | test("compares the hash in a CJS module with a custom condition", () => { 269 | const code = compile( 270 | "build/file.js", 271 | ` 272 | fetchQuery( 273 | environment, 274 | graphql\` 275 | query AppQuery($id: ID!) { 276 | user(id: $id) { 277 | name 278 | } 279 | } 280 | \`, 281 | {id: 4}, 282 | ) 283 | .subscribe({ 284 | start: console.log, 285 | complete: console.log, 286 | error: console.log, 287 | next: console.log 288 | }); 289 | `, 290 | { 291 | artifactDirectory: "./src/__generated__", 292 | devMode: true, 293 | module: "cjs", 294 | condition: "IS_DEV == true", 295 | } 296 | ); 297 | 298 | expect(code).toMatchSnapshot(); 299 | }); 300 | }); 301 | -------------------------------------------------------------------------------- /src/__tests__/fixtures/relay-config-file/relay.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | schema: "schema.server.graphql", 3 | src: "src", 4 | extensions: ["js"], 5 | include: ["**/*"], 6 | exclude: [], 7 | verbose: true, 8 | watchman: true, 9 | watch: false, 10 | validate: false, 11 | quiet: false, 12 | persistOutput: undefined, 13 | noFutureProofEnums: true, 14 | artifactDirectory: "src/__graphql__", 15 | customScalars: {}, 16 | }; 17 | -------------------------------------------------------------------------------- /src/__tests__/fixtures/relay-config-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "relay-config-typescript", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "relay": { 7 | "language": "typescript" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/__tests__/setup.test.ts: -------------------------------------------------------------------------------- 1 | import { setup } from "../setup"; 2 | import * as path from "path"; 3 | 4 | const fixtures = path.join(process.cwd(), "src", "__tests__", "fixtures"); 5 | 6 | test("use relay-config to automatically detect configuration", () => { 7 | const { options, compileOptions } = setup( 8 | { 9 | initialOptions: { 10 | absWorkingDir: path.join(fixtures, "relay-config-file"), 11 | }, 12 | } as any, 13 | {} 14 | ); 15 | 16 | expect(options.filter.toString()).toBe("/\\/.(js)\\//"); 17 | expect(compileOptions.artifactDirectory).toBe("src/__graphql__"); 18 | }); 19 | 20 | test("use relay-config to automatically detect TypeScript configuration", () => { 21 | const { options, compileOptions } = setup( 22 | { 23 | initialOptions: { 24 | absWorkingDir: path.join(fixtures, "relay-config-typescript"), 25 | }, 26 | } as any, 27 | {} 28 | ); 29 | 30 | expect(options.filter.toString()).toBe("/\\.tsx/"); 31 | expect(compileOptions.artifactDirectory).toBe("src/__generated__"); 32 | }); 33 | -------------------------------------------------------------------------------- /src/compile.ts: -------------------------------------------------------------------------------- 1 | import { print, parse } from "graphql"; 2 | import * as crypto from "crypto"; 3 | import * as path from "path"; 4 | 5 | export interface CompileOptions { 6 | artifactDirectory: string; 7 | condition?: string; 8 | devMode?: boolean; 9 | module?: "cjs" | "esm"; 10 | suffix?: string; 11 | buildCommand?: string; 12 | } 13 | 14 | const defaultOptions: Partial = { 15 | condition: "", 16 | devMode: false, 17 | module: "esm", 18 | suffix: "", 19 | buildCommand: "relay-compiler", 20 | }; 21 | 22 | export function compile( 23 | file: string, 24 | contents: string, 25 | opts: CompileOptions 26 | ): string { 27 | opts = Object.assign({}, defaultOptions, opts) as Required; 28 | 29 | const imports: string[] = []; 30 | 31 | contents = contents.replace(/graphql`([\s\S]*?)`/gm, (_match, query) => { 32 | const ast = parse(query); 33 | 34 | if (ast.definitions.length === 0) { 35 | throw new Error("Unexpected empty graphql tag."); 36 | } 37 | 38 | const definition = ast.definitions[0]; 39 | if ( 40 | definition.kind !== "FragmentDefinition" && 41 | definition.kind !== "OperationDefinition" 42 | ) { 43 | throw new Error( 44 | "Expected a fragment, mutation, query, or " + 45 | "subscription, got `" + 46 | definition.kind + 47 | "`." 48 | ); 49 | } 50 | 51 | const name = definition.name && definition.name.value; 52 | if (!name) { 53 | throw new Error("GraphQL operations and fragments must contain names"); 54 | } 55 | 56 | const hash = crypto 57 | .createHash("md5") 58 | .update(print(definition), "utf8") 59 | .digest("hex"); 60 | 61 | const id = `graphql__${hash}`; 62 | const importFile = `${name}.graphql${opts.suffix}`; 63 | const importPath = getRelativeImportPath( 64 | file, 65 | opts.artifactDirectory, 66 | importFile 67 | ); 68 | 69 | let result = id; 70 | 71 | if (opts.module === "esm") { 72 | imports.push(`import ${id} from "${importPath}";`); 73 | } else { 74 | result = `require("${importPath}")`; 75 | } 76 | 77 | if (opts.devMode) { 78 | const error = getErrorMessage(name, opts.buildCommand); 79 | const condition = opts.condition ? `${opts.condition} && ` : ""; 80 | 81 | if (opts.module === "cjs") { 82 | result = 83 | `${id} !== void 0 ? ${id} : (${id} = ${result}, ${condition}${id}.hash && ` + 84 | `${id}.hash !== "${hash}" && console.error("${error}"), ${id})`; 85 | } else if (opts.module == "esm") { 86 | result = 87 | `(${condition}${id}.hash && ${id}.hash !== "${hash}" && ` + 88 | `console.error("${error}"), ${id})`; 89 | } 90 | } 91 | 92 | return result; 93 | }); 94 | 95 | return (imports.length > 0 ? `${imports.join("\n")}\n` : "") + contents; 96 | } 97 | 98 | function getErrorMessage(name: string, buildCommand: string) { 99 | return ( 100 | `The definition of '${name}' appears to have changed. Run \`${buildCommand}\` to ` + 101 | `update the generated files to receive the expected data.` 102 | ); 103 | } 104 | 105 | function getRelativeImportPath( 106 | fileName: string, 107 | artifactDirectory: string, 108 | fileToRequire: string 109 | ): string { 110 | const relative = path.relative( 111 | path.dirname(fileName), 112 | path.resolve(artifactDirectory) 113 | ); 114 | 115 | const relativeReference = 116 | relative.length === 0 || !relative.startsWith(".") ? "./" : ""; 117 | 118 | return relativeReference + path.join(relative, fileToRequire); 119 | } 120 | -------------------------------------------------------------------------------- /src/index.cjs.ts: -------------------------------------------------------------------------------- 1 | import createRelayPlugin from "./index"; 2 | 3 | export = createRelayPlugin; 4 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from "esbuild"; 2 | import * as fs from "fs"; 3 | import { compile } from "./compile"; 4 | import { PluginOptions, setup } from "./setup"; 5 | 6 | export default function createRelayPlugin(opts?: PluginOptions): Plugin { 7 | return { 8 | name: "relay", 9 | setup(build) { 10 | const { options, compileOptions } = setup(build, opts); 11 | 12 | build.onLoad({ filter: options.filter }, async (args) => { 13 | let contents = await fs.promises.readFile(args.path, "utf8"); 14 | if (contents.includes("graphql`")) { 15 | contents = compile(args.path, contents, compileOptions); 16 | } 17 | 18 | return { 19 | contents: contents, 20 | loader: options.loader || "jsx", 21 | }; 22 | }); 23 | }, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/setup.ts: -------------------------------------------------------------------------------- 1 | import type { PluginBuild, Loader } from "esbuild"; 2 | import { CompileOptions } from "./compile"; 3 | 4 | export interface PluginOptions { 5 | artifactDirectory?: string; 6 | buildCommand?: string; 7 | condition?: string; 8 | devMode?: boolean; 9 | filter?: RegExp; 10 | module?: "cjs" | "esm"; 11 | suffix?: string; 12 | loader?: Loader; 13 | } 14 | 15 | export function setup( 16 | build: PluginBuild, 17 | opts: PluginOptions 18 | ): { options: Required; compileOptions: CompileOptions } { 19 | const currentCwd = process.cwd(); 20 | 21 | let relayConfig: any; 22 | try { 23 | process.chdir(build.initialOptions.absWorkingDir); 24 | relayConfig = 25 | typeof require === "function" ? require("relay-config").loadConfig() : {}; 26 | } catch (_err) { 27 | } finally { 28 | process.chdir(currentCwd); 29 | } 30 | 31 | relayConfig = Object.assign( 32 | { 33 | language: "javascript", 34 | artifactDirectory: "src/__generated__", 35 | }, 36 | relayConfig || {} 37 | ); 38 | 39 | let filter: RegExp; 40 | if (relayConfig.extensions && relayConfig.extensions.length) { 41 | filter = new RegExp(`/\.(${relayConfig.extensions.join("|")})/`); 42 | } else if (relayConfig.language == "typescript") { 43 | filter = /\.tsx/; 44 | } else { 45 | filter = /\.jsx/; 46 | } 47 | 48 | const options = Object.assign({ filter }, opts) as Required; 49 | const compileOptions = Object.assign( 50 | { 51 | artifactDirectory: relayConfig.artifactDirectory, 52 | devMode: process.env.NODE_ENV !== "production", 53 | module: "esm", 54 | }, 55 | opts 56 | ); 57 | 58 | return { options, compileOptions }; 59 | } 60 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "target": "es2017", 7 | "outDir": "lib", 8 | "declaration": true, 9 | "rootDir": "src", 10 | "lib": ["es2021"], 11 | "skipLibCheck": true 12 | }, 13 | "include": ["src"], 14 | "exclude": ["node_modules", "lib"] 15 | } 16 | --------------------------------------------------------------------------------