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 this.emitFile({
129 type: 'asset',
130 fileName: 'README.md',
131 source: await fs.readFile('README.md'),
132 });
133
134 this.emitFile({
135 type: 'asset',
136 fileName: 'LICENSE',
137 source: await fs.readFile('./LICENSE'),
138 });
139 },
140
141 async renderChunk(_code, { fileName }) {
142 const name = fileName.replace(/\.m?js$/, '');
143
144 const getContents = async (extension) => {
145 try {
146 const name = fileName.replace(/\.m?js$/, '');
147 const contents = await fs.readFile(
148 path.join(graphqlModule, name + extension)
149 );
150 return contents;
151 } catch (_error) {
152 return null;
153 }
154 };
155
156 const dts = await getContents('.d.ts');
157 const flow = await getContents('.js.flow');
158
159 if (dts) {
160 this.emitFile({
161 type: 'asset',
162 fileName: name + '.d.ts',
163 source: dts,
164 });
165 }
166
167 if (flow) {
168 this.emitFile({
169 type: 'asset',
170 fileName: name + '.js.flow',
171 source: flow,
172 });
173 }
174
175 return null;
176 },
177 },
178
179 resolve({
180 extensions: ['.mjs', '.js'],
181 mainFields: ['module', 'browser', 'main'],
182 preferBuiltins: false,
183 browser: true,
184 }),
185
186 babel({
187 babelrc: false,
188 babelHelpers: 'bundled',
189 presets: [],
190 plugins: [
191 babelTransformDevAssert,
192 babelTransformObjectFreeze,
193 babelTransformComputedProps,
194 babelModularGraphQL,
195 'reghex/babel',
196 ],
197 }),
198
199 buble({
200 transforms: {
201 unicodeRegExp: false,
202 dangerousForOf: true,
203 dangerousTaggedTemplateString: true,
204 asyncAwait: false,
205 },
206 objectAssign: 'Object.assign',
207 }),
208
209 replace({
210 preventAssignment: true,
211 values: {
212 'process.env.NODE_ENV': JSON.stringify('production'),
213 },
214 }),
215
216 terser({
217 warnings: true,
218 ecma: 5,
219 keep_fnames: true,
220 ie8: false,
221 compress: {
222 pure_getters: true,
223 toplevel: true,
224 booleans_as_integers: false,
225 keep_fnames: true,
226 keep_fargs: true,
227 if_return: false,
228 ie8: false,
229 sequences: false,
230 loops: false,
231 conditionals: false,
232 join_vars: false,
233 },
234 mangle: {
235 module: true,
236 keep_fnames: true,
237 },
238 output: {
239 beautify: true,
240 braces: true,
241 indent_level: 2,
242 },
243 }),
244 ],
245
246 treeshake: 'smallest',
247 shimMissingExports: false,
248 preserveEntrySignatures: 'allow-extension',
249
250 output: [
251 {
252 chunkFileNames: '[name].js',
253 entryFileNames: '[name].js',
254 dir: './dist',
255 exports: 'named',
256 format: 'cjs',
257 minifyInternalExports: false,
258 hoistTransitiveImports: false,
259 manualChunks,
260 },
261 {
262 chunkFileNames: '[name].mjs',
263 entryFileNames: '[name].mjs',
264 dir: './dist',
265 exports: 'named',
266 format: 'esm',
267 minifyInternalExports: false,
268 hoistTransitiveImports: false,
269 manualChunks,
270 },
271 ],
272};