1import fs from 'node:fs/promises';
2import path from 'node:path/posix';
3import { readFileSync } from 'node:fs';
4
5import * as prettier from 'prettier';
6import commonjs from '@rollup/plugin-commonjs';
7import resolve from '@rollup/plugin-node-resolve';
8import babel from '@rollup/plugin-babel';
9import terser from '@rollup/plugin-terser';
10import cjsCheck from 'rollup-plugin-cjs-check';
11import dts from 'rollup-plugin-dts';
12
13const normalize = name => []
14 .concat(name)
15 .join(' ')
16 .replace(/[@\s/.]+/g, ' ')
17 .trim()
18 .replace(/\s+/, '-')
19 .toLowerCase();
20
21const extension = name => {
22 if (/\.d.ts$/.test(name)) {
23 return '.d.ts';
24 } else if (/\.module.js$/.test(name)) {
25 return '.module.js';
26 } else {
27 return path.extname(name);
28 }
29};
30
31const meta = JSON.parse(readFileSync('package.json'));
32const name = normalize(meta.name);
33
34const externalModules = [
35 'typescript',
36 ...Object.keys(meta.dependencies || {}),
37 ...Object.keys(meta.peerDependencies || {}),
38];
39
40const external = new RegExp(`^(${externalModules.join('|')})($|/)`);
41
42const exports = {};
43for (const key in meta.exports) {
44 const entry = meta.exports[key];
45 if (typeof entry === 'object' && !!entry.source) {
46 const entryPath = normalize(key);
47 const entryName = normalize([name, entryPath]);
48 exports[entryName] = {
49 path: entryPath,
50 ...entry,
51 };
52 }
53}
54
55const commonConfig = {
56 input: Object.entries(exports).reduce((input, [exportName, entry]) => {
57 input[exportName] = entry.source;
58 return input;
59 }, {}),
60 onwarn: () => {},
61 external(id) {
62 return external.test(id);
63 },
64 treeshake: {
65 unknownGlobalSideEffects: false,
66 tryCatchDeoptimization: false,
67 moduleSideEffects: false,
68 },
69};
70
71const commonPlugins = [
72 resolve({
73 extensions: ['.mjs', '.js', '.ts'],
74 mainFields: ['module', 'jsnext', 'main'],
75 preferBuiltins: false,
76 browser: true,
77 }),
78
79 commonjs({
80 ignoreGlobal: true,
81 include: /\/node_modules\//,
82 }),
83];
84
85const commonOutput = {
86 dir: './',
87 exports: 'auto',
88 sourcemap: true,
89 sourcemapExcludeSources: false,
90 hoistTransitiveImports: false,
91 indent: false,
92 freeze: false,
93 strict: false,
94 generatedCode: {
95 preset: 'es5',
96 reservedNamesAsProps: false,
97 objectShorthand: false,
98 constBindings: false,
99 },
100};
101
102const outputPlugins = [
103 {
104 name: 'outputPackageJsons',
105 async writeBundle() {
106 for (const key in exports) {
107 const entry = exports[key];
108 if (entry.path) {
109 const output = path.relative(entry.path, process.cwd());
110 const json = JSON.stringify({
111 name: key,
112 private: true,
113 version: '0.0.0',
114 main: path.join(output, entry.require),
115 module: path.join(output, entry.import),
116 types: path.join(output, entry.types),
117 source: path.join(output, entry.source),
118 exports: {
119 '.': {
120 types: path.join(output, entry.types),
121 import: path.join(output, entry.import),
122 require: path.join(output, entry.require),
123 source: path.join(output, entry.source),
124 },
125 },
126 }, null, 2);
127
128 await fs.mkdir(entry.path, { recursive: true });
129 await fs.writeFile(path.join(entry.path, 'package.json'), json);
130 }
131 }
132 },
133 },
134
135 cjsCheck(),
136
137 terser({
138 warnings: true,
139 ecma: 2015,
140 keep_fnames: true,
141 ie8: false,
142 compress: {
143 pure_getters: true,
144 toplevel: true,
145 booleans_as_integers: false,
146 keep_fnames: true,
147 keep_fargs: true,
148 if_return: false,
149 ie8: false,
150 sequences: false,
151 loops: false,
152 conditionals: false,
153 join_vars: false,
154 },
155 mangle: {
156 module: true,
157 keep_fnames: true,
158 },
159 output: {
160 beautify: true,
161 braces: true,
162 indent_level: 2,
163 },
164 }),
165];
166
167export default [
168 {
169 ...commonConfig,
170 plugins: [
171 ...commonPlugins,
172 babel({
173 babelrc: false,
174 babelHelpers: 'bundled',
175 extensions: ['mjs', 'js', 'jsx', 'ts', 'tsx'],
176 exclude: 'node_modules/**',
177 presets: [],
178 plugins: [
179 '@babel/plugin-transform-typescript',
180 '@babel/plugin-transform-block-scoping',
181 ],
182 }),
183 ],
184 output: [
185 {
186 ...commonOutput,
187 format: 'esm',
188 chunkFileNames(chunk) {
189 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.mjs'}`;
190 },
191 entryFileNames(chunk) {
192 return chunk.isEntry
193 ? path.normalize(exports[chunk.name].import)
194 : `dist/[name].mjs`;
195 },
196 plugins: outputPlugins,
197 },
198 {
199 ...commonOutput,
200 format: 'cjs',
201 esModule: true,
202 externalLiveBindings: true,
203 chunkFileNames(chunk) {
204 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.js'}`;
205 },
206 entryFileNames(chunk) {
207 return chunk.isEntry
208 ? path.normalize(exports[chunk.name].require)
209 : `dist/[name].js`;
210 },
211 plugins: outputPlugins,
212 },
213 ],
214 },
215
216 {
217 ...commonConfig,
218 plugins: [
219 ...commonPlugins,
220 dts(),
221 ],
222 output: {
223 ...commonOutput,
224 sourcemap: false,
225 format: 'dts',
226 chunkFileNames(chunk) {
227 return `dist/chunks/[name]-chunk${extension(chunk.name) || '.d.ts'}`;
228 },
229 entryFileNames(chunk) {
230 return chunk.isEntry
231 ? path.normalize(exports[chunk.name].types)
232 : `dist/[name].d.ts`;
233 },
234 plugins: [
235 {
236 renderChunk(code, chunk) {
237 if (chunk.fileName.endsWith('d.ts')) {
238 const gqlImportRe = /(import\s+(?:[*\s{}\w\d]+)\s*from\s*'graphql';?)/g;
239 code = code.replace(gqlImportRe, x => '/*!@ts-ignore*/\n' + x);
240
241 code = prettier.format(code, {
242 filepath: chunk.fileName,
243 parser: 'typescript',
244 singleQuote: true,
245 tabWidth: 2,
246 printWidth: 100,
247 trailingComma: 'es5',
248 });
249
250 return code;
251 }
252 },
253 },
254 ],
255 },
256 },
257];