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 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] = (exports[name] || '') + `export { ${key} } from '${EXTERNAL}'\n`;
34
35 const parts = name.split('/');
36 for (let i = parts.length - 1; i > 0; i--) {
37 const name = `${parts.slice(0, i).join('/')}/index`;
38 const from = `./${parts.slice(i).join('/')}`;
39 exports[name] = (exports[name] || '') + `export { ${local} } from '${from}'\n`;
40 }
41
42 const index = `export { ${local} } from './${name}'\n`;
43 exports.index = (exports.index || '') + index;
44}
45
46const manualChunks = (id, utils) => {
47 let chunk;
48 if (id.startsWith(graphqlModule)) {
49 chunk = id.slice(graphqlModule.length);
50 } else if (id.startsWith(virtualModule)) {
51 chunk = id.slice(virtualModule.length);
52 } else if (id.startsWith(aliasModule)) {
53 chunk = id.slice(aliasModule.length);
54 }
55
56 if (chunk) {
57 return chunk.replace(/\.m?js$/, '');
58 }
59
60 const { importers } = utils.getModuleInfo(id);
61 return importers.length === 1
62 ? manualChunks(importers[0], utils)
63 : 'shared';
64};
65
66export default {
67 input:
68 Object.keys(exports).reduce((input, key) => {
69 input[key] = path.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))
84 return null;
85
86 const entry = path.relative(virtualModule, id).replace(/\.m?js$/, '');
87 return exports[entry] || null;
88 },
89
90 async resolveId(source, importer) {
91 if (!source.startsWith('.') && !source.startsWith('virtual/'))
92 return null;
93
94 const target = path.join(importer ? path.dirname(importer) : cwd, source);
95
96 const virtualEntry = path.relative(virtualModule, target);
97 if (!virtualEntry.startsWith('../')) {
98 const aliasSource = path.join(aliasModule, virtualEntry);
99 const alias = await this.resolve(aliasSource, undefined, { skipSelf: true });
100 return alias || target;
101 }
102
103 const graphqlEntry = path.relative(graphqlModule, target);
104 if (!graphqlEntry.startsWith('../')) {
105 const aliasSource = path.join(aliasModule, graphqlEntry);
106 const alias = await this.resolve(aliasSource, undefined, { skipSelf: true });
107 return alias || target;
108 }
109
110 return null;
111 },
112
113 async renderStart() {
114 this.emitFile({
115 type: 'asset',
116 fileName: 'package.json',
117 source: packageMetadata,
118 });
119 },
120
121 async renderChunk(_code, { fileName }) {
122 const name = fileName.replace(/\.m?js$/, '');
123
124 const getContents = async (extension) => {
125 try {
126 const name = fileName.replace(/\.m?js$/, '');
127 const contents = await fs.readFile(path.join(graphqlModule, name + extension));
128 return contents;
129 } catch (_error) {
130 return null;
131 }
132 }
133
134 const dts = await getContents('.d.ts');
135 const flow = await getContents('.js.flow');
136
137 if (dts) {
138 this.emitFile({
139 type: 'asset',
140 fileName: name + '.d.ts',
141 source: dts,
142 });
143 }
144
145 if (flow) {
146 this.emitFile({
147 type: 'asset',
148 fileName: name + '.js.flow',
149 source: flow,
150 });
151 }
152
153 return null;
154 },
155 },
156
157 resolve({
158 extensions: ['.mjs', '.js'],
159 mainFields: ['module', 'browser', 'main'],
160 preferBuiltins: false,
161 browser: true,
162 }),
163
164 babel({
165 babelrc: false,
166 babelHelpers: 'bundled',
167 presets: [],
168 plugins: [
169 babelTransformDevAssert,
170 babelTransformObjectFreeze,
171 babelTransformComputedProps,
172 babelModularGraphQL,
173 'reghex/babel',
174 ],
175 }),
176
177 buble({
178 transforms: {
179 unicodeRegExp: false,
180 dangerousForOf: true,
181 dangerousTaggedTemplateString: true,
182 asyncAwait: false,
183 },
184 objectAssign: 'Object.assign',
185 }),
186
187 terser({
188 warnings: true,
189 ecma: 5,
190 keep_fnames: true,
191 ie8: false,
192 compress: {
193 pure_getters: true,
194 toplevel: true,
195 booleans_as_integers: false,
196 keep_fnames: true,
197 keep_fargs: true,
198 if_return: false,
199 ie8: false,
200 sequences: false,
201 loops: false,
202 conditionals: false,
203 join_vars: false
204 },
205 mangle: {
206 module: true,
207 keep_fnames: true,
208 },
209 output: {
210 beautify: true,
211 braces: true,
212 indent_level: 2
213 }
214 }),
215 ],
216
217 treeshake: 'smallest',
218 shimMissingExports: false,
219 preserveEntrySignatures: 'allow-extension',
220
221 output: [
222 {
223 chunkFileNames: '[name].js',
224 entryFileNames: '[name].js',
225 dir: './dist',
226 exports: 'named',
227 format: 'cjs',
228 minifyInternalExports: false,
229 hoistTransitiveImports: false,
230 manualChunks,
231 },
232 {
233 chunkFileNames: '[name].mjs',
234 entryFileNames: '[name].mjs',
235 dir: './dist',
236 exports: 'named',
237 format: 'esm',
238 minifyInternalExports: false,
239 hoistTransitiveImports: false,
240 manualChunks,
241 },
242 ],
243};