Mirror: The small sibling of the graphql package, slimmed down for client-side libraries.
1import * as path from 'path';
2import { promises as fs } from 'fs';
3
4import resolve from '@rollup/plugin-node-resolve';
5import buble from '@rollup/plugin-buble';
6import { babel } from '@rollup/plugin-babel';
7import { terser } from 'rollup-plugin-terser';
8
9import babelModularGraphQL from 'babel-plugin-modular-graphql';
10import babelTransformComputedProps from '../babel/transformComputedProps.mjs';
11import babelTransformDevAssert from '../babel/transformDevAssert.mjs';
12import babelTransformObjectFreeze from '../babel/transformObjectFreeze.mjs';
13
14import { packageMetadata, version } from './packageMetadata';
15
16const cwd = process.cwd();
17const graphqlModule = path.join(cwd, 'node_modules/graphql/');
18const virtualModule = path.join(cwd, 'virtual/');
19const aliasModule = path.join(cwd, 'alias/');
20
21const EXTERNAL = 'graphql';
22const externalModules = ['dns', 'fs', 'path', 'url'];
23const externalPredicate = new RegExp(`^(${externalModules.join('|')})($|/)`);
24
25const exports = {};
26const importMap = require('./importMap.json');
27
28for (const key in importMap) {
29 const { from, local } = importMap[key];
30 if (/\/jsutils\//g.test(from)) continue;
31
32 const name = from.replace(/^graphql\//, '');
33 exports[name] =
34 (exports[name] || '') + `export { ${key} } from '${EXTERNAL}'\n`;
35
36 const parts = name.split('/');
37 for (let i = parts.length - 1; i > 0; i--) {
38 const name = `${parts.slice(0, i).join('/')}/index`;
39 const from = `./${parts.slice(i).join('/')}`;
40 exports[name] =
41 (exports[name] || '') + `export { ${local} } from '${from}'\n`;
42 }
43
44 const index = `export { ${local} } from './${name}'\n`;
45 exports.index = (exports.index || '') + index;
46}
47
48const manualChunks = (id, utils) => {
49 let chunk;
50 if (id.startsWith(graphqlModule)) {
51 chunk = id.slice(graphqlModule.length);
52 } else if (id.startsWith(virtualModule)) {
53 chunk = id.slice(virtualModule.length);
54 } else if (id.startsWith(aliasModule)) {
55 chunk = id.slice(aliasModule.length);
56 }
57
58 if (chunk) {
59 return chunk.replace(/\.m?js$/, '');
60 }
61
62 const { importers } = utils.getModuleInfo(id);
63 return importers.length === 1 ? manualChunks(importers[0], utils) : 'shared';
64};
65
66export default {
67 input: Object.keys(exports).reduce((input, key) => {
68 input[key] = path.join('./virtual', key);
69 return input;
70 }, {}),
71 external(id) {
72 return externalPredicate.test(id);
73 },
74 treeshake: {
75 unknownGlobalSideEffects: false,
76 tryCatchDeoptimization: false,
77 moduleSideEffects: false,
78 },
79 plugins: [
80 {
81 async load(id) {
82 if (!id.startsWith(virtualModule)) return null;
83 const entry = path.relative(virtualModule, id).replace(/\.m?js$/, '');
84 if (entry === 'version') return version;
85 return exports[entry] || null;
86 },
87
88 async resolveId(source, importer) {
89 if (!source.startsWith('.') && !source.startsWith('virtual/'))
90 return null;
91
92 const target = path.join(
93 importer ? path.dirname(importer) : cwd,
94 source
95 );
96
97 const virtualEntry = path.relative(virtualModule, target);
98 if (!virtualEntry.startsWith('../')) {
99 const aliasSource = path.join(aliasModule, virtualEntry);
100 const alias = await this.resolve(aliasSource, undefined, {
101 skipSelf: true,
102 });
103 return alias || target;
104 }
105
106 const graphqlEntry = path.relative(graphqlModule, target);
107 if (!graphqlEntry.startsWith('../')) {
108 const aliasSource = path.join(aliasModule, graphqlEntry);
109 const alias = await this.resolve(aliasSource, undefined, {
110 skipSelf: true,
111 });
112 return alias || target;
113 }
114
115 return null;
116 },
117
118 async renderStart() {
119 this.emitFile({
120 type: 'asset',
121 fileName: 'package.json',
122 source: packageMetadata,
123 });
124 },
125
126 async renderChunk(_code, { fileName }) {
127 const name = fileName.replace(/\.m?js$/, '');
128
129 const getContents = async (extension) => {
130 try {
131 const name = fileName.replace(/\.m?js$/, '');
132 const contents = await fs.readFile(
133 path.join(graphqlModule, name + extension)
134 );
135 return contents;
136 } catch (_error) {
137 return null;
138 }
139 };
140
141 const dts = await getContents('.d.ts');
142 const flow = await getContents('.js.flow');
143
144 if (dts) {
145 this.emitFile({
146 type: 'asset',
147 fileName: name + '.d.ts',
148 source: dts,
149 });
150 }
151
152 if (flow) {
153 this.emitFile({
154 type: 'asset',
155 fileName: name + '.js.flow',
156 source: flow,
157 });
158 }
159
160 return null;
161 },
162 },
163
164 resolve({
165 extensions: ['.mjs', '.js'],
166 mainFields: ['module', 'browser', 'main'],
167 preferBuiltins: false,
168 browser: true,
169 }),
170
171 babel({
172 babelrc: false,
173 babelHelpers: 'bundled',
174 presets: [],
175 plugins: [
176 babelTransformDevAssert,
177 babelTransformObjectFreeze,
178 babelTransformComputedProps,
179 babelModularGraphQL,
180 'reghex/babel',
181 ],
182 }),
183
184 buble({
185 transforms: {
186 unicodeRegExp: false,
187 dangerousForOf: true,
188 dangerousTaggedTemplateString: true,
189 asyncAwait: false,
190 },
191 objectAssign: 'Object.assign',
192 }),
193
194 terser({
195 warnings: true,
196 ecma: 5,
197 keep_fnames: true,
198 ie8: false,
199 compress: {
200 pure_getters: true,
201 toplevel: true,
202 booleans_as_integers: false,
203 keep_fnames: true,
204 keep_fargs: true,
205 if_return: false,
206 ie8: false,
207 sequences: false,
208 loops: false,
209 conditionals: false,
210 join_vars: false,
211 },
212 mangle: {
213 module: true,
214 keep_fnames: true,
215 },
216 output: {
217 beautify: true,
218 braces: true,
219 indent_level: 2,
220 },
221 }),
222 ],
223
224 treeshake: 'smallest',
225 shimMissingExports: false,
226 preserveEntrySignatures: 'allow-extension',
227
228 output: [
229 {
230 chunkFileNames: '[name].js',
231 entryFileNames: '[name].js',
232 dir: './dist',
233 exports: 'named',
234 format: 'cjs',
235 minifyInternalExports: false,
236 hoistTransitiveImports: false,
237 manualChunks,
238 },
239 {
240 chunkFileNames: '[name].mjs',
241 entryFileNames: '[name].mjs',
242 dir: './dist',
243 exports: 'named',
244 format: 'esm',
245 minifyInternalExports: false,
246 hoistTransitiveImports: false,
247 manualChunks,
248 },
249 ],
250};