Mirror: Modular GraphQL.js import paths without the hassle.
1const fs = require('fs');
2const path = require('path');
3
4const { rollup } = require('rollup');
5
6const cwd = process.cwd();
7const basepath = path.resolve(cwd, 'node_modules/graphql/');
8
9function generateImportMapPlugin(opts = {}) {
10 const maxDepth = opts.maxDepth || 2;
11 const filename = opts.filename || 'import-map.json';
12 const map = new Map();
13
14 const resolveFile = (from, to) => {
15 return path.join(from, to);
16 };
17
18 const resolveFromMap = (id, name, depth = 0) => {
19 const exports = map.get(id);
20 if (!exports || !exports.has(name)) return null;
21
22 const declaration = exports.get(name);
23 if (depth >= maxDepth || declaration.from === id) {
24 return declaration;
25 }
26
27 return resolveFromMap(declaration.from, declaration.local, depth + 1)
28 || declaration;
29 };
30
31 return {
32 name: 'generate-import-map',
33 transform(code, id) {
34 const relative = path.relative(basepath, id);
35 const dirname = path.dirname(relative);
36 const exports = new Map();
37
38 this.parse(code).body
39 .filter(x => x.type === 'ExportNamedDeclaration')
40 .forEach(node => {
41 const source = node.source
42 ? resolveFile(dirname, node.source.value)
43 : relative;
44
45 node.specifiers.forEach(specifier => {
46 exports.set(specifier.exported.name, {
47 local: specifier.local.name,
48 from: source
49 });
50 });
51
52 if (node.declaration) {
53 (node.declaration.declarations || [node.declaration])
54 .forEach(declaration => {
55 if (declaration && declaration.id) {
56 const { name } = declaration.id;
57 exports.set(declaration.id.name, {
58 local: name,
59 from: source
60 });
61 }
62 });
63 }
64 });
65
66 map.set(relative, exports);
67 return null;
68 },
69 renderChunk(_code, chunk) {
70 const id = chunk.facadeModuleId;
71 const relative = path.relative(basepath, id);
72
73 if (chunk.isEntry) {
74 const importMap = chunk.exports.reduce((acc, name) => {
75 const declaration = resolveFromMap(relative, name);
76 if (declaration) {
77 const dirname = path.join('graphql/', path.dirname(declaration.from));
78 const filename = path.basename(declaration.from, '.mjs');
79
80 acc[name] = {
81 local: declaration.local,
82 from: path.join(dirname, filename),
83 };
84 }
85
86 return acc;
87 }, {});
88
89 this.emitFile({
90 type: 'asset',
91 filename,
92 name: filename,
93 source: JSON.stringify(importMap, null, 2)
94 });
95 }
96 },
97 };
98}
99
100(async () => {
101 const bundle = await rollup({
102 input: path.join(basepath, 'index.mjs'),
103 external: (id) => !/^\.{0,2}\//.test(id),
104 preserveModules: true,
105 plugins: [
106 require('@rollup/plugin-node-resolve')(),
107 generateImportMapPlugin({
108 filename: 'import-map.json'
109 })
110 ],
111 });
112
113 const { output } = await bundle.generate({});
114
115 fs.writeFileSync(
116 path.resolve(cwd, 'import-map.json'),
117 output.find(asset => asset.type === 'asset').source
118 );
119})().catch((err) => {
120 console.error(`${err.name}: ${err.message}`);
121 process.exit(1);
122});