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.posix.join(cwd, 'node_modules/graphql/');
18const virtualModule = path.posix.join(cwd, 'virtual/');
19const aliasModule = path.posix.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.posix.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.posix
84 .relative(virtualModule, id)
85 .replace(/\.m?js$/, '');
86 if (entry === 'version') return version;
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.posix.join(
95 importer ? path.posix.dirname(importer) : cwd,
96 source
97 );
98
99 const virtualEntry = path.posix.relative(virtualModule, target);
100 if (!virtualEntry.startsWith('../')) {
101 const aliasSource = path.posix.join(aliasModule, virtualEntry);
102 const alias = await this.resolve(aliasSource, undefined, {
103 skipSelf: true,
104 });
105 return alias || target;
106 }
107
108 const graphqlEntry = path.posix.relative(graphqlModule, target);
109 if (!graphqlEntry.startsWith('../')) {
110 const aliasSource = path.posix.join(aliasModule, graphqlEntry);
111 const alias = await this.resolve(aliasSource, undefined, {
112 skipSelf: true,
113 });
114 return alias || target;
115 }
116
117 return null;
118 },
119
120 async renderStart() {
121 this.emitFile({
122 type: 'asset',
123 fileName: 'package.json',
124 source: packageMetadata,
125 });
126 },
127
128 async renderChunk(_code, { fileName }) {
129 const name = fileName.replace(/\.m?js$/, '');
130
131 const getContents = async (extension) => {
132 try {
133 const name = fileName.replace(/\.m?js$/, '');
134 const contents = await fs.readFile(
135 path.join(graphqlModule, name + extension)
136 );
137 return contents;
138 } catch (_error) {
139 return null;
140 }
141 };
142
143 const dts = await getContents('.d.ts');
144 const flow = await getContents('.js.flow');
145
146 if (dts) {
147 this.emitFile({
148 type: 'asset',
149 fileName: name + '.d.ts',
150 source: dts,
151 });
152 }
153
154 if (flow) {
155 this.emitFile({
156 type: 'asset',
157 fileName: name + '.js.flow',
158 source: flow,
159 });
160 }
161
162 return null;
163 },
164 },
165
166 resolve({
167 extensions: ['.mjs', '.js'],
168 mainFields: ['module', 'browser', 'main'],
169 preferBuiltins: false,
170 browser: true,
171 }),
172
173 babel({
174 babelrc: false,
175 babelHelpers: 'bundled',
176 presets: [],
177 plugins: [
178 babelTransformDevAssert,
179 babelTransformObjectFreeze,
180 babelTransformComputedProps,
181 babelModularGraphQL,
182 'reghex/babel',
183 ],
184 }),
185
186 buble({
187 transforms: {
188 unicodeRegExp: false,
189 dangerousForOf: true,
190 dangerousTaggedTemplateString: true,
191 asyncAwait: false,
192 },
193 objectAssign: 'Object.assign',
194 }),
195
196 terser({
197 warnings: true,
198 ecma: 5,
199 keep_fnames: true,
200 ie8: false,
201 compress: {
202 pure_getters: true,
203 toplevel: true,
204 booleans_as_integers: false,
205 keep_fnames: true,
206 keep_fargs: true,
207 if_return: false,
208 ie8: false,
209 sequences: false,
210 loops: false,
211 conditionals: false,
212 join_vars: false,
213 },
214 mangle: {
215 module: true,
216 keep_fnames: true,
217 },
218 output: {
219 beautify: true,
220 braces: true,
221 indent_level: 2,
222 },
223 }),
224 ],
225
226 treeshake: 'smallest',
227 shimMissingExports: false,
228 preserveEntrySignatures: 'allow-extension',
229
230 output: [
231 {
232 chunkFileNames: '[name].js',
233 entryFileNames: '[name].js',
234 dir: './dist',
235 exports: 'named',
236 format: 'cjs',
237 minifyInternalExports: false,
238 hoistTransitiveImports: false,
239 manualChunks,
240 },
241 {
242 chunkFileNames: '[name].mjs',
243 entryFileNames: '[name].mjs',
244 dir: './dist',
245 exports: 'named',
246 format: 'esm',
247 minifyInternalExports: false,
248 hoistTransitiveImports: false,
249 manualChunks,
250 },
251 ],
252};