Mirror: The spec-compliant minimum of client-side GraphQL.
1import fs from 'node:fs/promises';
2import path from 'node:path/posix';
3import { readFileSync } from 'node:fs';
4
5import * as prettier from 'prettier';
6import commonjs from '@rollup/plugin-commonjs';
7import resolve from '@rollup/plugin-node-resolve';
8import babel from '@rollup/plugin-babel';
9import terser from '@rollup/plugin-terser';
10import cjsCheck from 'rollup-plugin-cjs-check';
11import dts from 'rollup-plugin-dts';
12
13const normalize = name => []
14 .concat(name)
15 .join(' ')
16 .replace(/[@\s/.]+/g, ' ')
17 .trim()
18 .replace(/\s+/, '-')
19 .toLowerCase();
20
21const extension = name => {
22 if (/\.d.ts$/.test(name)) {
23 return '.d.ts';
24 } else {
25 return path.extname(name);
26 }
27};
28
29const meta = JSON.parse(readFileSync('package.json'));
30const name = normalize(meta.name);
31
32const externalModules = [
33 ...Object.keys(meta.dependencies || {}),
34 ...Object.keys(meta.peerDependencies || {}),
35];
36
37const external = new RegExp(`^(${externalModules.join('|')})($|/)`);
38
39const exports = {};
40for (const key in meta.exports) {
41 const entry = meta.exports[key];
42 if (typeof entry === 'object' && !!entry.source) {
43 const entryPath = normalize(key);
44 const entryName = normalize([name, entryPath]);
45 exports[entryName] = {
46 path: entryPath,
47 ...entry,
48 };
49 }
50}
51
52const commonConfig = {
53 input: Object.entries(exports).reduce((input, [exportName, entry]) => {
54 input[exportName] = entry.source;
55 return input;
56 }, {}),
57 onwarn: () => {},
58 external(id) {
59 return external.test(id);
60 },
61 treeshake: {
62 unknownGlobalSideEffects: false,
63 tryCatchDeoptimization: false,
64 moduleSideEffects: false,
65 },
66};
67
68const commonPlugins = [
69 resolve({
70 extensions: ['.mjs', '.js', '.ts'],
71 mainFields: ['module', 'jsnext', 'main'],
72 preferBuiltins: false,
73 browser: true,
74 }),
75
76 commonjs({
77 ignoreGlobal: true,
78 include: /\/node_modules\//,
79 }),
80];
81
82const commonOutput = {
83 dir: './',
84 exports: 'auto',
85 sourcemap: true,
86 sourcemapExcludeSources: false,
87 hoistTransitiveImports: false,
88 indent: false,
89 freeze: false,
90 strict: false,
91 generatedCode: {
92 preset: 'es5',
93 reservedNamesAsProps: false,
94 objectShorthand: false,
95 constBindings: false,
96 },
97};
98
99const outputPlugins = [
100 {
101 name: 'outputPackageJsons',
102 async writeBundle() {
103 for (const key in exports) {
104 const entry = exports[key];
105 if (entry.path) {
106 const output = path.relative(entry.path, process.cwd());
107 const json = JSON.stringify({
108 name: key,
109 private: true,
110 version: '0.0.0',
111 main: path.join(output, entry.require),
112 module: path.join(output, entry.import),
113 types: path.join(output, entry.types),
114 source: path.join(output, entry.source),
115 exports: {
116 '.': {
117 types: path.join(output, entry.types),
118 import: path.join(output, entry.import),
119 require: path.join(output, entry.require),
120 source: path.join(output, entry.source),
121 },
122 },
123 }, null, 2);
124
125 await fs.mkdir(entry.path, { recursive: true });
126 await fs.writeFile(path.join(entry.path, 'package.json'), json);
127 }
128 }
129 },
130 },
131
132 cjsCheck(),
133
134 terser({
135 warnings: true,
136 ecma: 2015,
137 keep_fnames: true,
138 ie8: false,
139 compress: {
140 pure_getters: true,
141 toplevel: true,
142 booleans_as_integers: false,
143 keep_fnames: true,
144 keep_fargs: true,
145 ie8: false,
146 conditionals: false,
147 join_vars: false,
148 },
149 mangle: {
150 module: true,
151 keep_fnames: true,
152 },
153 output: {
154 beautify: true,
155 braces: true,
156 indent_level: 2,
157 },
158 }),
159];
160
161export default [
162 {
163 ...commonConfig,
164 plugins: [
165 ...commonPlugins,
166 babel({
167 babelrc: false,
168 babelHelpers: 'bundled',
169 extensions: ['mjs', 'js', 'jsx', 'ts', 'tsx'],
170 exclude: 'node_modules/**',
171 presets: [],
172 plugins: [
173 '@babel/plugin-transform-typescript',
174 '@babel/plugin-transform-block-scoping',
175 ],
176 }),
177 ],
178 output: [
179 {
180 ...commonOutput,
181 format: 'esm',
182 chunkFileNames(chunk) {
183 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.mjs'}`;
184 },
185 entryFileNames(chunk) {
186 return chunk.isEntry
187 ? path.normalize(exports[chunk.name].import)
188 : `dist/[name].mjs`;
189 },
190 plugins: outputPlugins,
191 },
192 {
193 ...commonOutput,
194 format: 'cjs',
195 esModule: true,
196 externalLiveBindings: true,
197 chunkFileNames(chunk) {
198 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.js'}`;
199 },
200 entryFileNames(chunk) {
201 return chunk.isEntry
202 ? path.normalize(exports[chunk.name].require)
203 : `dist/[name].js`;
204 },
205 plugins: outputPlugins,
206 },
207 ],
208 },
209
210 {
211 ...commonConfig,
212 plugins: [
213 ...commonPlugins,
214 dts(),
215 ],
216 output: {
217 ...commonOutput,
218 sourcemap: false,
219 format: 'dts',
220 chunkFileNames(chunk) {
221 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.d.ts'}`;
222 },
223 entryFileNames(chunk) {
224 return chunk.isEntry
225 ? path.normalize(exports[chunk.name].types)
226 : `dist/[name].d.ts`;
227 },
228 plugins: [
229 {
230 renderChunk(code, chunk) {
231 if (chunk.fileName.endsWith('d.ts')) {
232 const gqlImportRe = /(import\s+(?:[*\s{}\w\d]+)\s*from\s*'graphql';?)/g;
233 code = code.replace(gqlImportRe, x => '/*@ts-ignore*/\n' + x);
234
235 code = prettier.format(code, {
236 filepath: chunk.fileName,
237 parser: 'typescript',
238 singleQuote: true,
239 tabWidth: 2,
240 printWidth: 100,
241 trailingComma: 'es5',
242 });
243
244 return code;
245 }
246 },
247 },
248 ],
249 },
250 },
251];