UNPKG

react-saasify-chrisvxd

Version:

React components for Saasify web clients.

594 lines (485 loc) 20.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); const path = require('path'); const mm = require('micromatch'); const t = require('@babel/types'); const template = require('@babel/template').default; const traverse = require('@babel/traverse').default; const rename = require('./renamer'); const _require = require('./utils'), getName = _require.getName, getIdentifier = _require.getIdentifier, getExportIdentifier = _require.getExportIdentifier; const WRAPPER_TEMPLATE = template(` var NAME = (function () { var exports = this; var module = {exports: this}; BODY; return module.exports; }).call({}); `); const ESMODULE_TEMPLATE = template(`exports.__esModule = true;`); const EXPORT_ASSIGN_TEMPLATE = template('EXPORTS.NAME = LOCAL'); const EXPORT_ALL_TEMPLATE = template('$parcel$exportWildcard(OLD_NAME, $parcel$require(ID, SOURCE))'); const REQUIRE_CALL_TEMPLATE = template('$parcel$require(ID, SOURCE)'); const REQUIRE_RESOLVE_CALL_TEMPLATE = template('$parcel$require$resolve(ID, SOURCE)'); const TYPEOF = { module: 'object', require: 'function' }; function hasSideEffects(asset, { sideEffects } = asset._package) { switch (typeof sideEffects) { case 'undefined': return true; case 'boolean': return sideEffects; case 'string': return mm.isMatch(path.relative(asset._package.pkgdir, asset.name), sideEffects, { matchBase: true }); case 'object': return sideEffects.some(sideEffects => hasSideEffects(asset, { sideEffects })); } } module.exports = { Program: { enter(path, asset) { traverse.cache.clearScope(); path.scope.crawl(); asset.cacheData.imports = asset.cacheData.imports || Object.create(null); asset.cacheData.exports = asset.cacheData.exports || Object.create(null); asset.cacheData.wildcards = asset.cacheData.wildcards || []; asset.cacheData.sideEffects = asset._package && hasSideEffects(asset); let shouldWrap = false; path.traverse({ CallExpression(path) { // If we see an `eval` call, wrap the module in a function. // Otherwise, local variables accessed inside the eval won't work. let callee = path.node.callee; if (t.isIdentifier(callee) && callee.name === 'eval' && !path.scope.hasBinding('eval', true)) { asset.cacheData.isCommonJS = true; shouldWrap = true; path.stop(); } }, ReturnStatement(path) { // Wrap in a function if we see a top-level return statement. if (!path.getFunctionParent()) { shouldWrap = true; asset.cacheData.isCommonJS = true; path.replaceWith(t.returnStatement(t.memberExpression(t.identifier('module'), t.identifier('exports')))); path.stop(); } }, ReferencedIdentifier(path) { // We must wrap if `module` is referenced as a free identifier rather // than a statically resolvable member expression. if (path.node.name === 'module' && (!path.parentPath.isMemberExpression() || path.parent.computed) && !(path.parentPath.isUnaryExpression() && path.parent.operator === 'typeof') && !path.scope.hasBinding('module') && !path.scope.getData('shouldWrap')) { asset.cacheData.isCommonJS = true; shouldWrap = true; path.stop(); } } }); path.scope.setData('shouldWrap', shouldWrap); }, exit(path, asset) { let scope = path.scope; if (scope.getData('shouldWrap')) { if (asset.cacheData.isES6Module) { path.unshiftContainer('body', [ESMODULE_TEMPLATE()]); } path.replaceWith(t.program([WRAPPER_TEMPLATE({ NAME: getExportsIdentifier(asset), BODY: path.node.body })])); asset.cacheData.exports = {}; asset.cacheData.isCommonJS = true; asset.cacheData.isES6Module = false; } else { // Re-crawl scope so we are sure to have all bindings. scope.crawl(); // Rename each binding in the top-level scope to something unique. for (let name in scope.bindings) { if (!name.startsWith('$' + t.toIdentifier(asset.id))) { let newName = getName(asset, 'var', name); rename(scope, name, newName); } } let exportsIdentifier = getExportsIdentifier(asset); // Add variable that represents module.exports if it is referenced and not declared. if (scope.hasGlobal(exportsIdentifier.name) && !scope.hasBinding(exportsIdentifier.name)) { scope.push({ id: exportsIdentifier, init: t.objectExpression([]) }); } } path.stop(); asset.isAstDirty = true; } }, DirectiveLiteral(path) { // Remove 'use strict' directives, since modules are concatenated - one strict mode // module should not apply to all other modules in the same scope. if (path.node.value === 'use strict') { path.parentPath.remove(); } }, MemberExpression(path, asset) { if (path.scope.hasBinding('module') || path.scope.getData('shouldWrap')) { return; } if (t.matchesPattern(path.node, 'module.exports')) { path.replaceWith(getExportsIdentifier(asset)); asset.cacheData.isCommonJS = true; } if (t.matchesPattern(path.node, 'module.id')) { path.replaceWith(t.stringLiteral(asset.id)); } if (t.matchesPattern(path.node, 'module.hot')) { path.replaceWith(t.identifier('null')); } if (t.matchesPattern(path.node, 'module.require') && asset.options.target !== 'node') { path.replaceWith(t.identifier('null')); } if (t.matchesPattern(path.node, 'module.bundle')) { path.replaceWith(t.identifier('require')); } }, ReferencedIdentifier(path, asset) { if (path.node.name === 'exports' && !path.scope.hasBinding('exports') && !path.scope.getData('shouldWrap')) { path.replaceWith(getExportsIdentifier(asset)); asset.cacheData.isCommonJS = true; } if (path.node.name === 'global' && !path.scope.hasBinding('global')) { path.replaceWith(t.identifier('$parcel$global')); asset.globals.delete('global'); } let globalCode = asset.globals.get(path.node.name); if (globalCode) { path.scope.getProgramParent().path.unshiftContainer('body', [template(globalCode)()]); asset.globals.delete(path.node.name); } }, ThisExpression(path, asset) { if (!path.scope.parent && !path.scope.getData('shouldWrap')) { path.replaceWith(getExportsIdentifier(asset)); asset.cacheData.isCommonJS = true; } }, AssignmentExpression(path, asset) { if (path.scope.hasBinding('exports') || path.scope.getData('shouldWrap')) { return; } let _path$node = path.node, left = _path$node.left, right = _path$node.right; if (t.isIdentifier(left) && left.name === 'exports') { path.get('left').replaceWith(getExportsIdentifier(asset)); asset.cacheData.isCommonJS = true; } // If we can statically evaluate the name of a CommonJS export, create an ES6-style export for it. // This allows us to remove the CommonJS export object completely in many cases. if (t.isMemberExpression(left) && t.isIdentifier(left.object, { name: 'exports' }) && (t.isIdentifier(left.property) && !left.computed || t.isStringLiteral(left.property))) { let name = t.isIdentifier(left.property) ? left.property.name : left.property.value; let identifier = getExportIdentifier(asset, name); // Replace the CommonJS assignment with a reference to the ES6 identifier. path.get('left.object').replaceWith(getExportsIdentifier(asset)); path.get('right').replaceWith(identifier); // If this is the first assignment, create a binding for the ES6-style export identifier. // Otherwise, assign to the existing export binding. let scope = path.scope.getProgramParent(); if (!scope.hasBinding(identifier.name)) { asset.cacheData.exports[name] = identifier.name; // If in the program scope, create a variable declaration and initialize with the exported value. // Otherwise, declare the variable in the program scope, and assign to it here. if (path.scope === scope) { let _path$insertBefore = path.insertBefore(t.variableDeclaration('var', [t.variableDeclarator(t.clone(identifier), right)])), _path$insertBefore2 = (0, _slicedToArray2.default)(_path$insertBefore, 1), decl = _path$insertBefore2[0]; scope.registerDeclaration(decl); } else { scope.push({ id: t.clone(identifier) }); path.insertBefore(t.assignmentExpression('=', t.clone(identifier), right)); } } else { path.insertBefore(t.assignmentExpression('=', t.clone(identifier), right)); } asset.cacheData.isCommonJS = true; } }, UnaryExpression(path) { // Replace `typeof module` with "object" if (path.node.operator === 'typeof' && t.isIdentifier(path.node.argument) && TYPEOF[path.node.argument.name] && !path.scope.hasBinding(path.node.argument.name) && !path.scope.getData('shouldWrap')) { path.replaceWith(t.stringLiteral(TYPEOF[path.node.argument.name])); } }, CallExpression(path, asset) { let _path$node2 = path.node, callee = _path$node2.callee, args = _path$node2.arguments; let ignore = args.length !== 1 || !t.isStringLiteral(args[0]) || path.scope.hasBinding('require'); if (ignore) { return; } if (t.isIdentifier(callee, { name: 'require' })) { let source = args[0].value; // Ignore require calls that were ignored earlier. if (!asset.dependencies.has(source)) { return; } // If this require call does not occur in the top-level, e.g. in a function // or inside an if statement, or if it might potentially happen conditionally, // the module must be wrapped in a function so that the module execution order is correct. let parent = path.getStatementParent().parentPath; let bail = path.findParent(p => p.isConditionalExpression() || p.isLogicalExpression() || p.isSequenceExpression()); if (!parent.isProgram() || bail) { asset.dependencies.get(source).shouldWrap = true; } asset.cacheData.imports['$require$' + source] = [source, '*']; // Generate a variable name based on the current asset id and the module name to require. // This will be replaced by the final variable name of the resolved asset in the packager. path.replaceWith(REQUIRE_CALL_TEMPLATE({ ID: t.stringLiteral(asset.id), SOURCE: t.stringLiteral(args[0].value) })); } if (t.matchesPattern(callee, 'require.resolve')) { path.replaceWith(REQUIRE_RESOLVE_CALL_TEMPLATE({ ID: t.stringLiteral(asset.id), SOURCE: args[0] })); } }, ImportDeclaration(path, asset) { // For each specifier, rename the local variables to point to the imported name. // This will be replaced by the final variable name of the resolved asset in the packager. var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = path.node.specifiers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { let specifier = _step.value; let id = getIdentifier(asset, 'import', specifier.local.name); rename(path.scope, specifier.local.name, id.name); if (t.isImportDefaultSpecifier(specifier)) { asset.cacheData.imports[id.name] = [path.node.source.value, 'default']; } else if (t.isImportSpecifier(specifier)) { asset.cacheData.imports[id.name] = [path.node.source.value, specifier.imported.name]; } else if (t.isImportNamespaceSpecifier(specifier)) { asset.cacheData.imports[id.name] = [path.node.source.value, '*']; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } addImport(asset, path); path.remove(); }, ExportDefaultDeclaration(path, asset) { let declaration = path.node.declaration; let identifier = getExportIdentifier(asset, 'default'); let name = declaration.id ? declaration.id.name : declaration.name; if (asset.cacheData.imports[name]) { asset.cacheData.exports['default'] = asset.cacheData.imports[name]; identifier = t.identifier(name); } if (hasExport(asset, name)) { identifier = t.identifier(name); } // Add assignment to exports object for namespace imports and commonjs. path.insertAfter(EXPORT_ASSIGN_TEMPLATE({ EXPORTS: getExportsIdentifier(asset, path.scope), NAME: t.identifier('default'), LOCAL: t.clone(identifier) })); if (t.isIdentifier(declaration)) { // Rename the variable being exported. safeRename(path, asset, declaration.name, identifier.name); path.remove(); } else if (t.isExpression(declaration) || !declaration.id) { // Declare a variable to hold the exported value. path.replaceWith(t.variableDeclaration('var', [t.variableDeclarator(identifier, t.toExpression(declaration))])); path.scope.registerDeclaration(path); } else { // Rename the declaration to the exported name. safeRename(path, asset, declaration.id.name, identifier.name); path.replaceWith(declaration); } if (!asset.cacheData.exports['default']) { asset.cacheData.exports['default'] = identifier.name; } // Mark the asset as an ES6 module, so we handle imports correctly in the packager. asset.cacheData.isES6Module = true; }, ExportNamedDeclaration(path, asset) { let _path$node3 = path.node, declaration = _path$node3.declaration, source = _path$node3.source, specifiers = _path$node3.specifiers; if (source) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = specifiers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { let specifier = _step2.value; let exported = specifier.exported; if (t.isExportDefaultSpecifier(specifier)) { asset.cacheData.exports[exported.name] = [source.value, 'default']; } else if (t.isExportNamespaceSpecifier(specifier)) { asset.cacheData.exports[exported.name] = [source.value, '*']; } else if (t.isExportSpecifier(specifier)) { asset.cacheData.exports[exported.name] = [source.value, specifier.local.name]; } let id = getIdentifier(asset, 'import', exported.name); asset.cacheData.imports[id.name] = asset.cacheData.exports[exported.name]; path.insertAfter(EXPORT_ASSIGN_TEMPLATE({ EXPORTS: getExportsIdentifier(asset, path.scope), NAME: exported, LOCAL: id })); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return != null) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } addImport(asset, path); path.remove(); } else if (declaration) { path.replaceWith(declaration); let identifiers = t.isIdentifier(declaration.id) ? [declaration.id] : t.getBindingIdentifiers(declaration); for (let id in identifiers) { addExport(asset, path, identifiers[id], identifiers[id]); } } else if (specifiers.length > 0) { var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = specifiers[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { let specifier = _step3.value; addExport(asset, path, specifier.local, specifier.exported); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return != null) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } path.remove(); } // Mark the asset as an ES6 module, so we handle imports correctly in the packager. asset.cacheData.isES6Module = true; }, ExportAllDeclaration(path, asset) { asset.cacheData.wildcards.push(path.node.source.value); asset.cacheData.isES6Module = true; path.replaceWith(EXPORT_ALL_TEMPLATE({ OLD_NAME: getExportsIdentifier(asset), SOURCE: t.stringLiteral(path.node.source.value), ID: t.stringLiteral(asset.id) })); } }; function addImport(asset, path) { // Replace with a $parcel$require call so we know where to insert side effects. let requireCall = REQUIRE_CALL_TEMPLATE({ ID: t.stringLiteral(asset.id), SOURCE: t.stringLiteral(path.node.source.value) }); // Hoist the call to the top of the file. let lastImport = path.scope.getData('hoistedImport'); if (lastImport) { var _lastImport$insertAft = lastImport.insertAfter(requireCall); var _lastImport$insertAft2 = (0, _slicedToArray2.default)(_lastImport$insertAft, 1); lastImport = _lastImport$insertAft2[0]; } else { var _path$parentPath$unsh = path.parentPath.unshiftContainer('body', [requireCall]); var _path$parentPath$unsh2 = (0, _slicedToArray2.default)(_path$parentPath$unsh, 1); lastImport = _path$parentPath$unsh2[0]; } path.scope.setData('hoistedImport', lastImport); } function addExport(asset, path, local, exported) { let scope = path.scope.getProgramParent(); let identifier = getExportIdentifier(asset, exported.name); if (asset.cacheData.imports[local.name]) { asset.cacheData.exports[exported.name] = asset.cacheData.imports[local.name]; identifier = t.identifier(local.name); } if (hasExport(asset, local.name)) { identifier = t.identifier(local.name); } let assignNode = EXPORT_ASSIGN_TEMPLATE({ EXPORTS: getExportsIdentifier(asset, scope), NAME: t.identifier(exported.name), LOCAL: identifier }); let binding = scope.getBinding(local.name); let constantViolations = binding ? binding.constantViolations.concat(path) : [path]; if (!asset.cacheData.exports[exported.name]) { asset.cacheData.exports[exported.name] = identifier.name; } try { rename(scope, local.name, identifier.name); } catch (e) { throw new Error('export ' + e.message); } constantViolations.forEach(path => path.insertAfter(t.cloneDeep(assignNode))); } function hasExport(asset, name) { let exports = asset.cacheData.exports; return Object.keys(exports).some(k => exports[k] === name); } function safeRename(path, asset, from, to) { if (from === to) { return; } // If the binding that we're renaming is constant, it's safe to rename it. // Otherwise, create a new binding that references the original. let binding = path.scope.getBinding(from); if (binding && binding.constant) { rename(path.scope, from, to); } else { let _path$insertAfter = path.insertAfter(t.variableDeclaration('var', [t.variableDeclarator(t.identifier(to), t.identifier(from))])), _path$insertAfter2 = (0, _slicedToArray2.default)(_path$insertAfter, 1), decl = _path$insertAfter2[0]; path.scope.getBinding(from).reference(decl.get('declarations.0.init')); path.scope.registerDeclaration(decl); } } function getExportsIdentifier(asset, scope) { if (scope && scope.getData('shouldWrap')) { return t.identifier('exports'); } else { return getIdentifier(asset, 'exports'); } }