├── .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 | [](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 |
19 | {(data.books || []).map((book) => (
20 |
21 | ))}
22 |
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 |
--------------------------------------------------------------------------------