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 if_return: false,
146 ie8: false,
147 sequences: false,
148 loops: false,
149 conditionals: false,
150 join_vars: false,
151 },
152 mangle: {
153 module: true,
154 keep_fnames: true,
155 },
156 output: {
157 beautify: true,
158 braces: true,
159 indent_level: 2,
160 },
161 }),
162];
163
164export default [
165 {
166 ...commonConfig,
167 plugins: [
168 ...commonPlugins,
169 babel({
170 babelrc: false,
171 babelHelpers: 'bundled',
172 extensions: ['mjs', 'js', 'jsx', 'ts', 'tsx'],
173 exclude: 'node_modules/**',
174 presets: [],
175 plugins: [
176 '@babel/plugin-transform-typescript',
177 '@babel/plugin-transform-block-scoping',
178 ],
179 }),
180 ],
181 output: [
182 {
183 ...commonOutput,
184 format: 'esm',
185 chunkFileNames(chunk) {
186 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.mjs'}`;
187 },
188 entryFileNames(chunk) {
189 return chunk.isEntry
190 ? path.normalize(exports[chunk.name].import)
191 : `dist/[name].mjs`;
192 },
193 plugins: outputPlugins,
194 },
195 {
196 ...commonOutput,
197 format: 'cjs',
198 esModule: true,
199 externalLiveBindings: true,
200 chunkFileNames(chunk) {
201 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.js'}`;
202 },
203 entryFileNames(chunk) {
204 return chunk.isEntry
205 ? path.normalize(exports[chunk.name].require)
206 : `dist/[name].js`;
207 },
208 plugins: outputPlugins,
209 },
210 ],
211 },
212
213 {
214 ...commonConfig,
215 plugins: [
216 ...commonPlugins,
217 dts(),
218 ],
219 output: {
220 ...commonOutput,
221 sourcemap: false,
222 format: 'dts',
223 chunkFileNames(chunk) {
224 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.d.ts'}`;
225 },
226 entryFileNames(chunk) {
227 return chunk.isEntry
228 ? path.normalize(exports[chunk.name].types)
229 : `dist/[name].d.ts`;
230 },
231 plugins: [
232 {
233 renderChunk(code, chunk) {
234 if (chunk.fileName.endsWith('d.ts')) {
235 const gqlImportRe = /(import\s+(?:[*\s{}\w\d]+)\s*from\s*'graphql';?)/g;
236 code = code.replace(gqlImportRe, x => '/*@ts-ignore*/\n' + x);
237
238 code = prettier.format(code, {
239 filepath: chunk.fileName,
240 parser: 'typescript',
241 singleQuote: true,
242 tabWidth: 2,
243 printWidth: 100,
244 trailingComma: 'es5',
245 });
246
247 return code;
248 }
249 },
250 },
251 ],
252 },
253 },
254];