1import { resolve, basename } from 'path';
2import commonjs from '@rollup/plugin-commonjs';
3import nodeResolve from '@rollup/plugin-node-resolve';
4import typescript from 'rollup-plugin-typescript2';
5import buble from '@rollup/plugin-buble';
6import babel from 'rollup-plugin-babel';
7import { terser } from 'rollup-plugin-terser';
8import compiler from '@ampproject/rollup-plugin-closure-compiler';
9
10const cwd = process.cwd();
11const pkgInfo = require('./package.json');
12const name = basename(pkgInfo.main, '.js');
13
14const terserPretty = terser({
15 sourcemap: true,
16 warnings: true,
17 ecma: 5,
18 keep_fnames: true,
19 ie8: false,
20 compress: {
21 pure_getters: true,
22 toplevel: true,
23 booleans_as_integers: false,
24 keep_fnames: true,
25 keep_fargs: true,
26 if_return: false,
27 ie8: false,
28 sequences: false,
29 loops: false,
30 conditionals: false,
31 join_vars: false,
32 },
33 mangle: false,
34 output: {
35 beautify: true,
36 braces: true,
37 indent_level: 2,
38 },
39});
40
41const terserMinified = terser({
42 sourcemap: true,
43 warnings: true,
44 ecma: 5,
45 ie8: false,
46 toplevel: true,
47 mangle: true,
48 compress: {
49 keep_infinity: true,
50 pure_getters: true,
51 passes: 10,
52 },
53 output: {
54 comments: false,
55 },
56});
57
58const importAllPlugin = ({ types: t }) => ({
59 visitor: {
60 VariableDeclarator(path) {
61 if (
62 t.isIdentifier(path.node.id) &&
63 t.isCallExpression(path.node.init) &&
64 t.isIdentifier(path.node.init.callee) &&
65 path.node.init.callee.name === 'require' &&
66 path.node.init.arguments.length === 1
67 ) {
68 path.parentPath.replaceWith(
69 t.importDeclaration(
70 [t.importNamespaceSpecifier(path.node.id)],
71 path.node.init.arguments[0]
72 )
73 );
74 }
75 },
76 },
77});
78
79const unwrapStatePlugin = ({ types: t }) => ({
80 pre() {
81 this.props = new Map();
82 this.test = (node) =>
83 /state$/i.test(node.id.name) ||
84 (node.init.properties.length === 1 && node.init.properties[0].key.name === 'contents');
85 },
86 visitor: {
87 VariableDeclarator(path) {
88 if (
89 t.isIdentifier(path.node.id) &&
90 t.isObjectExpression(path.node.init) &&
91 path.node.init.properties.every(
92 (prop) => t.isObjectProperty(prop) && t.isIdentifier(prop.key)
93 ) &&
94 this.test(path.node)
95 ) {
96 const id = path.node.id.name;
97 const properties = path.node.init.properties;
98 const propNames = new Set(properties.map((x) => x.key.name));
99 const decl = properties.map((prop) => {
100 const key = `${id}$${prop.key.name}`;
101 return t.variableDeclarator(t.identifier(key), prop.value);
102 });
103
104 this.props.set(id, propNames);
105 path.parentPath.replaceWithMultiple(t.variableDeclaration('let', decl));
106 }
107 },
108 MemberExpression(path) {
109 if (
110 t.isIdentifier(path.node.object) &&
111 this.props.has(path.node.object.name) &&
112 t.isIdentifier(path.node.property) &&
113 this.props.get(path.node.object.name).has(path.node.property.name)
114 ) {
115 const id = path.node.object.name;
116 const propName = path.node.property.name;
117 path.replaceWith(t.identifier(`${id}$${propName}`));
118 }
119 },
120 },
121});
122
123const curryGuaranteePlugin = ({ types: t }) => {
124 const curryFnName = /^_(\d)$/;
125 const lengthId = t.identifier('length');
126 const bindId = t.identifier('bind');
127
128 return {
129 visitor: {
130 CallExpression(path) {
131 if (
132 !t.isMemberExpression(path.node.callee) ||
133 !t.isIdentifier(path.node.callee.object) ||
134 !t.isIdentifier(path.node.callee.property) ||
135 !path.node.callee.object.name === 'Curry' ||
136 !curryFnName.test(path.node.callee.property.name)
137 )
138 return;
139
140 const callFn = path.node.arguments[0];
141 const callArgs = path.node.arguments.slice(1);
142 if (t.isExpressionStatement(path.parent)) {
143 path.replaceWith(t.callExpression(callFn, callArgs));
144 return;
145 }
146
147 const arityLiteral = t.numericLiteral(callArgs.length);
148 const argIds = callArgs.map((init) => {
149 if (t.isIdentifier(init)) return init;
150 const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
151 path.scope.push({ id, init });
152 return id;
153 });
154
155 path.replaceWith(
156 t.conditionalExpression(
157 t.binaryExpression('===', t.memberExpression(callFn, lengthId), arityLiteral),
158 t.callExpression(callFn, argIds),
159 t.callExpression(t.memberExpression(callFn, bindId), [t.nullLiteral()].concat(argIds))
160 )
161 );
162 },
163 },
164 };
165};
166
167const makePlugins = (isProduction) =>
168 [
169 babel({
170 babelrc: false,
171 extensions: ['ts', 'tsx', 'js'],
172 exclude: 'node_modules/**',
173 presets: [],
174 plugins: ['@babel/plugin-syntax-typescript', importAllPlugin],
175 }),
176 typescript({
177 typescript: require('typescript'),
178 cacheRoot: './node_modules/.cache/.rts2_cache',
179 useTsconfigDeclarationDir: true,
180 tsconfigOverride: {
181 compilerOptions: {
182 strict: false,
183 noUnusedParameters: false,
184 declaration: !isProduction,
185 declarationDir: resolve(cwd, './dist/types/'),
186 target: 'esnext',
187 module: 'es2015',
188 rootDir: cwd,
189 },
190 },
191 }),
192 commonjs({
193 ignoreGlobal: true,
194 include: ['*', '**'],
195 extensions: ['.js', '.ts', '.tsx'],
196 }),
197 nodeResolve({
198 mainFields: ['module', 'jsnext', 'main'],
199 extensions: ['.js', '.ts', '.tsx'],
200 browser: true,
201 }),
202 buble({
203 transforms: {
204 unicodeRegExp: false,
205 dangerousForOf: true,
206 dangerousTaggedTemplateString: true,
207 },
208 objectAssign: 'Object.assign',
209 exclude: 'node_modules/**',
210 }),
211 babel({
212 babelrc: false,
213 extensions: ['ts', 'tsx', 'js'],
214 exclude: 'node_modules/**',
215 presets: [],
216 plugins: ['babel-plugin-closure-elimination', unwrapStatePlugin, curryGuaranteePlugin],
217 }),
218 compiler({
219 formatting: 'PRETTY_PRINT',
220 compilation_level: 'SIMPLE_OPTIMIZATIONS',
221 }),
222 isProduction ? terserMinified : terserPretty,
223 ].filter(Boolean);
224
225const config = {
226 input: './src/Wonka.ts',
227 onwarn: () => {},
228 external: () => false,
229 treeshake: {
230 propertyReadSideEffects: false,
231 },
232};
233
234export default [
235 {
236 ...config,
237 plugins: makePlugins(false),
238 output: [
239 {
240 legacy: true,
241 freeze: false,
242 esModule: false,
243 file: `./dist/${name}.js`,
244 format: 'cjs',
245 },
246 {
247 compact: true,
248 file: `./dist/${name}.mjs`,
249 format: 'esm',
250 },
251 ],
252 },
253 {
254 ...config,
255 plugins: makePlugins(true),
256 output: [
257 {
258 legacy: true,
259 freeze: false,
260 esModule: false,
261 file: `./dist/${name}.min.js`,
262 format: 'cjs',
263 },
264 ],
265 },
266];