source: imaps-frontend/node_modules/react-refresh/cjs/react-refresh-babel.development.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 26.2 KB
RevLine 
[d565449]1/**
2 * @license React
3 * react-refresh-babel.development.js
4 *
5 * Copyright (c) Facebook, Inc. and its affiliates.
6 *
7 * This source code is licensed under the MIT license found in the
8 * LICENSE file in the root directory of this source tree.
9 */
10
11'use strict';
12
13if (process.env.NODE_ENV !== "production") {
14 (function() {
15'use strict';
16
17function ReactFreshBabelPlugin (babel) {
18 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
19
20 if (typeof babel.env === 'function') {
21 // Only available in Babel 7.
22 var env = babel.env();
23
24 if (env !== 'development' && !opts.skipEnvCheck) {
25 throw new Error('React Refresh Babel transform should only be enabled in development environment. ' + 'Instead, the environment is: "' + env + '". If you want to override this check, pass {skipEnvCheck: true} as plugin options.');
26 }
27 }
28
29 var t = babel.types;
30 var refreshReg = t.identifier(opts.refreshReg || '$RefreshReg$');
31 var refreshSig = t.identifier(opts.refreshSig || '$RefreshSig$');
32 var registrationsByProgramPath = new Map();
33
34 function createRegistration(programPath, persistentID) {
35 var handle = programPath.scope.generateUidIdentifier('c');
36
37 if (!registrationsByProgramPath.has(programPath)) {
38 registrationsByProgramPath.set(programPath, []);
39 }
40
41 var registrations = registrationsByProgramPath.get(programPath);
42 registrations.push({
43 handle: handle,
44 persistentID: persistentID
45 });
46 return handle;
47 }
48
49 function isComponentishName(name) {
50 return typeof name === 'string' && name[0] >= 'A' && name[0] <= 'Z';
51 }
52
53 function findInnerComponents(inferredName, path, callback) {
54 var node = path.node;
55
56 switch (node.type) {
57 case 'Identifier':
58 {
59 if (!isComponentishName(node.name)) {
60 return false;
61 } // export default hoc(Foo)
62 // const X = hoc(Foo)
63
64
65 callback(inferredName, node, null);
66 return true;
67 }
68
69 case 'FunctionDeclaration':
70 {
71 // function Foo() {}
72 // export function Foo() {}
73 // export default function Foo() {}
74 callback(inferredName, node.id, null);
75 return true;
76 }
77
78 case 'ArrowFunctionExpression':
79 {
80 if (node.body.type === 'ArrowFunctionExpression') {
81 return false;
82 } // let Foo = () => {}
83 // export default hoc1(hoc2(() => {}))
84
85
86 callback(inferredName, node, path);
87 return true;
88 }
89
90 case 'FunctionExpression':
91 {
92 // let Foo = function() {}
93 // const Foo = hoc1(forwardRef(function renderFoo() {}))
94 // export default memo(function() {})
95 callback(inferredName, node, path);
96 return true;
97 }
98
99 case 'CallExpression':
100 {
101 var argsPath = path.get('arguments');
102
103 if (argsPath === undefined || argsPath.length === 0) {
104 return false;
105 }
106
107 var calleePath = path.get('callee');
108
109 switch (calleePath.node.type) {
110 case 'MemberExpression':
111 case 'Identifier':
112 {
113 var calleeSource = calleePath.getSource();
114 var firstArgPath = argsPath[0];
115 var innerName = inferredName + '$' + calleeSource;
116 var foundInside = findInnerComponents(innerName, firstArgPath, callback);
117
118 if (!foundInside) {
119 return false;
120 } // const Foo = hoc1(hoc2(() => {}))
121 // export default memo(React.forwardRef(function() {}))
122
123
124 callback(inferredName, node, path);
125 return true;
126 }
127
128 default:
129 {
130 return false;
131 }
132 }
133 }
134
135 case 'VariableDeclarator':
136 {
137 var init = node.init;
138
139 if (init === null) {
140 return false;
141 }
142
143 var name = node.id.name;
144
145 if (!isComponentishName(name)) {
146 return false;
147 }
148
149 switch (init.type) {
150 case 'ArrowFunctionExpression':
151 case 'FunctionExpression':
152 // Likely component definitions.
153 break;
154
155 case 'CallExpression':
156 {
157 // Maybe a HOC.
158 // Try to determine if this is some form of import.
159 var callee = init.callee;
160 var calleeType = callee.type;
161
162 if (calleeType === 'Import') {
163 return false;
164 } else if (calleeType === 'Identifier') {
165 if (callee.name.indexOf('require') === 0) {
166 return false;
167 } else if (callee.name.indexOf('import') === 0) {
168 return false;
169 } // Neither require nor import. Might be a HOC.
170 // Pass through.
171
172 }
173
174 break;
175 }
176
177 case 'TaggedTemplateExpression':
178 // Maybe something like styled.div`...`
179 break;
180
181 default:
182 return false;
183 }
184
185 var initPath = path.get('init');
186
187 var _foundInside = findInnerComponents(inferredName, initPath, callback);
188
189 if (_foundInside) {
190 return true;
191 } // See if this identifier is used in JSX. Then it's a component.
192
193
194 var binding = path.scope.getBinding(name);
195
196 if (binding === undefined) {
197 return;
198 }
199
200 var isLikelyUsedAsType = false;
201 var referencePaths = binding.referencePaths;
202
203 for (var i = 0; i < referencePaths.length; i++) {
204 var ref = referencePaths[i];
205
206 if (ref.node && ref.node.type !== 'JSXIdentifier' && ref.node.type !== 'Identifier') {
207 continue;
208 }
209
210 var refParent = ref.parent;
211
212 if (refParent.type === 'JSXOpeningElement') {
213 isLikelyUsedAsType = true;
214 } else if (refParent.type === 'CallExpression') {
215 var _callee = refParent.callee;
216 var fnName = void 0;
217
218 switch (_callee.type) {
219 case 'Identifier':
220 fnName = _callee.name;
221 break;
222
223 case 'MemberExpression':
224 fnName = _callee.property.name;
225 break;
226 }
227
228 switch (fnName) {
229 case 'createElement':
230 case 'jsx':
231 case 'jsxDEV':
232 case 'jsxs':
233 isLikelyUsedAsType = true;
234 break;
235 }
236 }
237
238 if (isLikelyUsedAsType) {
239 // const X = ... + later <X />
240 callback(inferredName, init, initPath);
241 return true;
242 }
243 }
244 }
245 }
246
247 return false;
248 }
249
250 function isBuiltinHook(hookName) {
251 switch (hookName) {
252 case 'useState':
253 case 'React.useState':
254 case 'useReducer':
255 case 'React.useReducer':
256 case 'useEffect':
257 case 'React.useEffect':
258 case 'useLayoutEffect':
259 case 'React.useLayoutEffect':
260 case 'useMemo':
261 case 'React.useMemo':
262 case 'useCallback':
263 case 'React.useCallback':
264 case 'useRef':
265 case 'React.useRef':
266 case 'useContext':
267 case 'React.useContext':
268 case 'useImperativeHandle':
269 case 'React.useImperativeHandle':
270 case 'useDebugValue':
271 case 'React.useDebugValue':
272 return true;
273
274 default:
275 return false;
276 }
277 }
278
279 function getHookCallsSignature(functionNode) {
280 var fnHookCalls = hookCalls.get(functionNode);
281
282 if (fnHookCalls === undefined) {
283 return null;
284 }
285
286 return {
287 key: fnHookCalls.map(function (call) {
288 return call.name + '{' + call.key + '}';
289 }).join('\n'),
290 customHooks: fnHookCalls.filter(function (call) {
291 return !isBuiltinHook(call.name);
292 }).map(function (call) {
293 return t.cloneDeep(call.callee);
294 })
295 };
296 }
297
298 var hasForceResetCommentByFile = new WeakMap(); // We let user do /* @refresh reset */ to reset state in the whole file.
299
300 function hasForceResetComment(path) {
301 var file = path.hub.file;
302 var hasForceReset = hasForceResetCommentByFile.get(file);
303
304 if (hasForceReset !== undefined) {
305 return hasForceReset;
306 }
307
308 hasForceReset = false;
309 var comments = file.ast.comments;
310
311 for (var i = 0; i < comments.length; i++) {
312 var cmt = comments[i];
313
314 if (cmt.value.indexOf('@refresh reset') !== -1) {
315 hasForceReset = true;
316 break;
317 }
318 }
319
320 hasForceResetCommentByFile.set(file, hasForceReset);
321 return hasForceReset;
322 }
323
324 function createArgumentsForSignature(node, signature, scope) {
325 var key = signature.key,
326 customHooks = signature.customHooks;
327 var forceReset = hasForceResetComment(scope.path);
328 var customHooksInScope = [];
329 customHooks.forEach(function (callee) {
330 // Check if a corresponding binding exists where we emit the signature.
331 var bindingName;
332
333 switch (callee.type) {
334 case 'MemberExpression':
335 if (callee.object.type === 'Identifier') {
336 bindingName = callee.object.name;
337 }
338
339 break;
340
341 case 'Identifier':
342 bindingName = callee.name;
343 break;
344 }
345
346 if (scope.hasBinding(bindingName)) {
347 customHooksInScope.push(callee);
348 } else {
349 // We don't have anything to put in the array because Hook is out of scope.
350 // Since it could potentially have been edited, remount the component.
351 forceReset = true;
352 }
353 });
354 var finalKey = key;
355
356 if (typeof require === 'function' && !opts.emitFullSignatures) {
357 // Prefer to hash when we can (e.g. outside of ASTExplorer).
358 // This makes it deterministically compact, even if there's
359 // e.g. a useState initializer with some code inside.
360 // We also need it for www that has transforms like cx()
361 // that don't understand if something is part of a string.
362 finalKey = require('crypto').createHash('sha1').update(key).digest('base64');
363 }
364
365 var args = [node, t.stringLiteral(finalKey)];
366
367 if (forceReset || customHooksInScope.length > 0) {
368 args.push(t.booleanLiteral(forceReset));
369 }
370
371 if (customHooksInScope.length > 0) {
372 args.push( // TODO: We could use an arrow here to be more compact.
373 // However, don't do it until AMA can run them natively.
374 t.functionExpression(null, [], t.blockStatement([t.returnStatement(t.arrayExpression(customHooksInScope))])));
375 }
376
377 return args;
378 }
379
380 function findHOCCallPathsAbove(path) {
381 var calls = [];
382
383 while (true) {
384 if (!path) {
385 return calls;
386 }
387
388 var parentPath = path.parentPath;
389
390 if (!parentPath) {
391 return calls;
392 }
393
394 if ( // hoc(_c = function() { })
395 parentPath.node.type === 'AssignmentExpression' && path.node === parentPath.node.right) {
396 // Ignore registrations.
397 path = parentPath;
398 continue;
399 }
400
401 if ( // hoc1(hoc2(...))
402 parentPath.node.type === 'CallExpression' && path.node !== parentPath.node.callee) {
403 calls.push(parentPath);
404 path = parentPath;
405 continue;
406 }
407
408 return calls; // Stop at other types.
409 }
410 }
411
412 var seenForRegistration = new WeakSet();
413 var seenForSignature = new WeakSet();
414 var seenForOutro = new WeakSet();
415 var hookCalls = new WeakMap();
416 var HookCallsVisitor = {
417 CallExpression: function (path) {
418 var node = path.node;
419 var callee = node.callee; // Note: this visitor MUST NOT mutate the tree in any way.
420 // It runs early in a separate traversal and should be very fast.
421
422 var name = null;
423
424 switch (callee.type) {
425 case 'Identifier':
426 name = callee.name;
427 break;
428
429 case 'MemberExpression':
430 name = callee.property.name;
431 break;
432 }
433
434 if (name === null || !/^use[A-Z]/.test(name)) {
435 return;
436 }
437
438 var fnScope = path.scope.getFunctionParent();
439
440 if (fnScope === null) {
441 return;
442 } // This is a Hook call. Record it.
443
444
445 var fnNode = fnScope.block;
446
447 if (!hookCalls.has(fnNode)) {
448 hookCalls.set(fnNode, []);
449 }
450
451 var hookCallsForFn = hookCalls.get(fnNode);
452 var key = '';
453
454 if (path.parent.type === 'VariableDeclarator') {
455 // TODO: if there is no LHS, consider some other heuristic.
456 key = path.parentPath.get('id').getSource();
457 } // Some built-in Hooks reset on edits to arguments.
458
459
460 var args = path.get('arguments');
461
462 if (name === 'useState' && args.length > 0) {
463 // useState second argument is initial state.
464 key += '(' + args[0].getSource() + ')';
465 } else if (name === 'useReducer' && args.length > 1) {
466 // useReducer second argument is initial state.
467 key += '(' + args[1].getSource() + ')';
468 }
469
470 hookCallsForFn.push({
471 callee: path.node.callee,
472 name: name,
473 key: key
474 });
475 }
476 };
477 return {
478 visitor: {
479 ExportDefaultDeclaration: function (path) {
480 var node = path.node;
481 var decl = node.declaration;
482 var declPath = path.get('declaration');
483
484 if (decl.type !== 'CallExpression') {
485 // For now, we only support possible HOC calls here.
486 // Named function declarations are handled in FunctionDeclaration.
487 // Anonymous direct exports like export default function() {}
488 // are currently ignored.
489 return;
490 } // Make sure we're not mutating the same tree twice.
491 // This can happen if another Babel plugin replaces parents.
492
493
494 if (seenForRegistration.has(node)) {
495 return;
496 }
497
498 seenForRegistration.add(node); // Don't mutate the tree above this point.
499 // This code path handles nested cases like:
500 // export default memo(() => {})
501 // In those cases it is more plausible people will omit names
502 // so they're worth handling despite possible false positives.
503 // More importantly, it handles the named case:
504 // export default memo(function Named() {})
505
506 var inferredName = '%default%';
507 var programPath = path.parentPath;
508 findInnerComponents(inferredName, declPath, function (persistentID, targetExpr, targetPath) {
509 if (targetPath === null) {
510 // For case like:
511 // export default hoc(Foo)
512 // we don't want to wrap Foo inside the call.
513 // Instead we assume it's registered at definition.
514 return;
515 }
516
517 var handle = createRegistration(programPath, persistentID);
518 targetPath.replaceWith(t.assignmentExpression('=', handle, targetExpr));
519 });
520 },
521 FunctionDeclaration: {
522 enter: function (path) {
523 var node = path.node;
524 var programPath;
525 var insertAfterPath;
526 var modulePrefix = '';
527
528 switch (path.parent.type) {
529 case 'Program':
530 insertAfterPath = path;
531 programPath = path.parentPath;
532 break;
533
534 case 'TSModuleBlock':
535 insertAfterPath = path;
536 programPath = insertAfterPath.parentPath.parentPath;
537 break;
538
539 case 'ExportNamedDeclaration':
540 insertAfterPath = path.parentPath;
541 programPath = insertAfterPath.parentPath;
542 break;
543
544 case 'ExportDefaultDeclaration':
545 insertAfterPath = path.parentPath;
546 programPath = insertAfterPath.parentPath;
547 break;
548
549 default:
550 return;
551 } // These types can be nested in typescript namespace
552 // We need to find the export chain
553 // Or return if it stays local
554
555
556 if (path.parent.type === 'TSModuleBlock' || path.parent.type === 'ExportNamedDeclaration') {
557 while (programPath.type !== 'Program') {
558 if (programPath.type === 'TSModuleDeclaration') {
559 if (programPath.parentPath.type !== 'Program' && programPath.parentPath.type !== 'ExportNamedDeclaration') {
560 return;
561 }
562
563 modulePrefix = programPath.node.id.name + '$' + modulePrefix;
564 }
565
566 programPath = programPath.parentPath;
567 }
568 }
569
570 var id = node.id;
571
572 if (id === null) {
573 // We don't currently handle anonymous default exports.
574 return;
575 }
576
577 var inferredName = id.name;
578
579 if (!isComponentishName(inferredName)) {
580 return;
581 } // Make sure we're not mutating the same tree twice.
582 // This can happen if another Babel plugin replaces parents.
583
584
585 if (seenForRegistration.has(node)) {
586 return;
587 }
588
589 seenForRegistration.add(node); // Don't mutate the tree above this point.
590
591 var innerName = modulePrefix + inferredName; // export function Named() {}
592 // function Named() {}
593
594 findInnerComponents(innerName, path, function (persistentID, targetExpr) {
595 var handle = createRegistration(programPath, persistentID);
596 insertAfterPath.insertAfter(t.expressionStatement(t.assignmentExpression('=', handle, targetExpr)));
597 });
598 },
599 exit: function (path) {
600 var node = path.node;
601 var id = node.id;
602
603 if (id === null) {
604 return;
605 }
606
607 var signature = getHookCallsSignature(node);
608
609 if (signature === null) {
610 return;
611 } // Make sure we're not mutating the same tree twice.
612 // This can happen if another Babel plugin replaces parents.
613
614
615 if (seenForSignature.has(node)) {
616 return;
617 }
618
619 seenForSignature.add(node); // Don't mutate the tree above this point.
620
621 var sigCallID = path.scope.generateUidIdentifier('_s');
622 path.scope.parent.push({
623 id: sigCallID,
624 init: t.callExpression(refreshSig, [])
625 }); // The signature call is split in two parts. One part is called inside the function.
626 // This is used to signal when first render happens.
627
628 path.get('body').unshiftContainer('body', t.expressionStatement(t.callExpression(sigCallID, []))); // The second call is around the function itself.
629 // This is used to associate a type with a signature.
630 // Unlike with $RefreshReg$, this needs to work for nested
631 // declarations too. So we need to search for a path where
632 // we can insert a statement rather than hard coding it.
633
634 var insertAfterPath = null;
635 path.find(function (p) {
636 if (p.parentPath.isBlock()) {
637 insertAfterPath = p;
638 return true;
639 }
640 });
641
642 if (insertAfterPath === null) {
643 return;
644 }
645
646 insertAfterPath.insertAfter(t.expressionStatement(t.callExpression(sigCallID, createArgumentsForSignature(id, signature, insertAfterPath.scope))));
647 }
648 },
649 'ArrowFunctionExpression|FunctionExpression': {
650 exit: function (path) {
651 var node = path.node;
652 var signature = getHookCallsSignature(node);
653
654 if (signature === null) {
655 return;
656 } // Make sure we're not mutating the same tree twice.
657 // This can happen if another Babel plugin replaces parents.
658
659
660 if (seenForSignature.has(node)) {
661 return;
662 }
663
664 seenForSignature.add(node); // Don't mutate the tree above this point.
665
666 var sigCallID = path.scope.generateUidIdentifier('_s');
667 path.scope.parent.push({
668 id: sigCallID,
669 init: t.callExpression(refreshSig, [])
670 }); // The signature call is split in two parts. One part is called inside the function.
671 // This is used to signal when first render happens.
672
673 if (path.node.body.type !== 'BlockStatement') {
674 path.node.body = t.blockStatement([t.returnStatement(path.node.body)]);
675 }
676
677 path.get('body').unshiftContainer('body', t.expressionStatement(t.callExpression(sigCallID, []))); // The second call is around the function itself.
678 // This is used to associate a type with a signature.
679
680 if (path.parent.type === 'VariableDeclarator') {
681 var insertAfterPath = null;
682 path.find(function (p) {
683 if (p.parentPath.isBlock()) {
684 insertAfterPath = p;
685 return true;
686 }
687 });
688
689 if (insertAfterPath === null) {
690 return;
691 } // Special case when a function would get an inferred name:
692 // let Foo = () => {}
693 // let Foo = function() {}
694 // We'll add signature it on next line so that
695 // we don't mess up the inferred 'Foo' function name.
696
697
698 insertAfterPath.insertAfter(t.expressionStatement(t.callExpression(sigCallID, createArgumentsForSignature(path.parent.id, signature, insertAfterPath.scope)))); // Result: let Foo = () => {}; __signature(Foo, ...);
699 } else {
700 // let Foo = hoc(() => {})
701 var paths = [path].concat(findHOCCallPathsAbove(path));
702 paths.forEach(function (p) {
703 p.replaceWith(t.callExpression(sigCallID, createArgumentsForSignature(p.node, signature, p.scope)));
704 }); // Result: let Foo = __signature(hoc(__signature(() => {}, ...)), ...)
705 }
706 }
707 },
708 VariableDeclaration: function (path) {
709 var node = path.node;
710 var programPath;
711 var insertAfterPath;
712 var modulePrefix = '';
713
714 switch (path.parent.type) {
715 case 'Program':
716 insertAfterPath = path;
717 programPath = path.parentPath;
718 break;
719
720 case 'TSModuleBlock':
721 insertAfterPath = path;
722 programPath = insertAfterPath.parentPath.parentPath;
723 break;
724
725 case 'ExportNamedDeclaration':
726 insertAfterPath = path.parentPath;
727 programPath = insertAfterPath.parentPath;
728 break;
729
730 case 'ExportDefaultDeclaration':
731 insertAfterPath = path.parentPath;
732 programPath = insertAfterPath.parentPath;
733 break;
734
735 default:
736 return;
737 } // These types can be nested in typescript namespace
738 // We need to find the export chain
739 // Or return if it stays local
740
741
742 if (path.parent.type === 'TSModuleBlock' || path.parent.type === 'ExportNamedDeclaration') {
743 while (programPath.type !== 'Program') {
744 if (programPath.type === 'TSModuleDeclaration') {
745 if (programPath.parentPath.type !== 'Program' && programPath.parentPath.type !== 'ExportNamedDeclaration') {
746 return;
747 }
748
749 modulePrefix = programPath.node.id.name + '$' + modulePrefix;
750 }
751
752 programPath = programPath.parentPath;
753 }
754 } // Make sure we're not mutating the same tree twice.
755 // This can happen if another Babel plugin replaces parents.
756
757
758 if (seenForRegistration.has(node)) {
759 return;
760 }
761
762 seenForRegistration.add(node); // Don't mutate the tree above this point.
763
764 var declPaths = path.get('declarations');
765
766 if (declPaths.length !== 1) {
767 return;
768 }
769
770 var declPath = declPaths[0];
771 var inferredName = declPath.node.id.name;
772 var innerName = modulePrefix + inferredName;
773 findInnerComponents(innerName, declPath, function (persistentID, targetExpr, targetPath) {
774 if (targetPath === null) {
775 // For case like:
776 // export const Something = hoc(Foo)
777 // we don't want to wrap Foo inside the call.
778 // Instead we assume it's registered at definition.
779 return;
780 }
781
782 var handle = createRegistration(programPath, persistentID);
783
784 if (targetPath.parent.type === 'VariableDeclarator') {
785 // Special case when a variable would get an inferred name:
786 // let Foo = () => {}
787 // let Foo = function() {}
788 // let Foo = styled.div``;
789 // We'll register it on next line so that
790 // we don't mess up the inferred 'Foo' function name.
791 // (eg: with @babel/plugin-transform-react-display-name or
792 // babel-plugin-styled-components)
793 insertAfterPath.insertAfter(t.expressionStatement(t.assignmentExpression('=', handle, declPath.node.id))); // Result: let Foo = () => {}; _c1 = Foo;
794 } else {
795 // let Foo = hoc(() => {})
796 targetPath.replaceWith(t.assignmentExpression('=', handle, targetExpr)); // Result: let Foo = hoc(_c1 = () => {})
797 }
798 });
799 },
800 Program: {
801 enter: function (path) {
802 // This is a separate early visitor because we need to collect Hook calls
803 // and "const [foo, setFoo] = ..." signatures before the destructuring
804 // transform mangles them. This extra traversal is not ideal for perf,
805 // but it's the best we can do until we stop transpiling destructuring.
806 path.traverse(HookCallsVisitor);
807 },
808 exit: function (path) {
809 var registrations = registrationsByProgramPath.get(path);
810
811 if (registrations === undefined) {
812 return;
813 } // Make sure we're not mutating the same tree twice.
814 // This can happen if another Babel plugin replaces parents.
815
816
817 var node = path.node;
818
819 if (seenForOutro.has(node)) {
820 return;
821 }
822
823 seenForOutro.add(node); // Don't mutate the tree above this point.
824
825 registrationsByProgramPath.delete(path);
826 var declarators = [];
827 path.pushContainer('body', t.variableDeclaration('var', declarators));
828 registrations.forEach(function (_ref) {
829 var handle = _ref.handle,
830 persistentID = _ref.persistentID;
831 path.pushContainer('body', t.expressionStatement(t.callExpression(refreshReg, [handle, t.stringLiteral(persistentID)])));
832 declarators.push(t.variableDeclarator(handle));
833 });
834 }
835 }
836 }
837 };
838}
839
840module.exports = ReactFreshBabelPlugin;
841 })();
842}
Note: See TracBrowser for help on using the repository browser.