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