UNPKG

babel-plugin-wildcard

Version:
222 lines (183 loc) 10.2 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = function (babel) { var t = babel.types; return { visitor: { ImportDeclaration: function ImportDeclaration(path, state) { var node = path.node, dec = void 0; var src = path.node.source.value; // Don't do anything if not a relative path // if if not a relative path then a module if (src[0] !== "." && src[0] !== "/") return; var addWildcard = false, // True if should perform transform wildcardName = void 0; // Name of the variable the wilcard will go in // not set if you have a filter { A, B, C } var filterNames = []; // e.g. A, B, C // has a /* specifing explicitly to use wildcard var wildcardRegex = /\/([^\/]*\*[^\/]*)$/; var isExplicitWildcard = wildcardRegex.test(src); var filenameRegex = new RegExp('.+'); // in the above case we need to remove the trailing /* if (isExplicitWildcard) { var lastSlash = path.node.source.value.lastIndexOf('/'); src = path.node.source.value.substring(0, lastSlash); var filenameGlob = path.node.source.value.substring(lastSlash + 1); path.node.source.value = src; filenameRegex = filenameGlob.replace(/[*\.\(\[\)\]]/g, function (character) { switch (character) { case '*': return '.*'; case '(': case ')': case '[': case ']': case '.': return '\\' + character; } return character; }); filenameRegex = new RegExp(filenameRegex); } // Get current filename so we can try to determine the folder var name = state.file.opts.filename; var files = []; var isDirs = []; var dir = _path3.default.join(_path3.default.dirname(name), src); // path of the target dir. for (var i = node.specifiers.length - 1; i >= 0; i--) { dec = node.specifiers[i]; if (t.isImportNamespaceSpecifier(dec) && _fs3.default.existsSync(dir) && !_fs3.default.statSync(dir).isFile() && !_fs3.default.existsSync(_path3.default.join(dir, 'index.js'))) { addWildcard = true; wildcardName = node.specifiers[i].local.name; node.specifiers.splice(i, 1); } // This handles { A, B, C } from 'C/*' if (t.isImportSpecifier(dec) && isExplicitWildcard) { // original: the actual name to lookup // local: the name to import as, may be same as original // We do this because of `import { A as B }` filterNames.push({ original: dec.imported.name, local: dec.local.name }); addWildcard = true; // Remove the specifier node.specifiers.splice(i, 1); } } // If they are no specifies but it is explicit if (isExplicitWildcard) { addWildcard = true; wildcardName = null; } // All the extensions that we should look at var exts = state.opts.exts || ["js", "es6", "es", "jsx"]; if (addWildcard) { // Add the original object. `import * as A from 'foo';` // this creates `const A = {};` // For filters this will be empty anyway if (filterNames.length === 0 && wildcardName !== null) { var obj = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(wildcardName), t.objectExpression([]))]); path.insertBefore(obj); } // Will throw if the path does not point to a dir try { var r = _fs3.default.readdirSync(dir); for (var i = 0; i < r.length; i++) { // Check extension is of one of the aboves var _path$parse = _path3.default.parse(r[i]), _name = _path$parse.name, ext = _path$parse.ext; if (exts.indexOf(ext.substring(1)) > -1 && filenameRegex.test(_name)) { files.push(r[i]); isDirs.push(!ext); } } } catch (e) { console.warn('Wildcard for ' + name + ' points at ' + src + ' which is not a directory.'); return; } // This is quite a mess but it essentially formats the file // extension, and adds it to the object for (var i = 0; i < files.length; i++) { // name of temp. variable to store import before moved // to object var id = path.scope.generateUidIdentifier("wcImport"); var file = files[i]; var isDir = isDirs[i]; // Strip extension var fancyName = file.replace(/(?!^)\.[^.\s]+$/, ""); // Handle dotfiles, remove prefix `.` in that case if (fancyName[0] === ".") { fancyName = fancyName.substring(1); } // If we're allowed to camel case, which is default, we run it // through this regex which converts it to a PascalCase variable. if (state.opts.noModifyCase !== true) { var parts = fancyName.match(/[A-Z][a-z]+(?![a-z])|[A-Z]+(?![a-z])|([a-zA-Z\d]+(?=-))|[a-zA-Z\d]+(?=_)|[a-z]+(?=[A-Z])|[A-Za-z0-9]+/g); if (state.opts.useCamelCase) { fancyName = parts[0].toLowerCase() + parts.slice(1).map(function (s) { return s[0].toUpperCase() + s.substring(1); }).join(""); } else { fancyName = parts.map(function (s) { return s[0].toUpperCase() + s.substring(1); }).join(""); } } // Now we're 100% settled on the fancyName, if the user // has provided a filer, we will check it: if (filterNames.length > 0) { // Find a filter name var res = null; for (var j = 0; j < filterNames.length; j++) { if (filterNames[j].original === fancyName) { res = filterNames[j]; break; } } if (res === null) continue; fancyName = res.local; } // This will remove file extensions from the generated `import`. // This is useful if your src/ files are for example .jsx or // .es6 but your generated files are of a different extension. // For situations like webpack you may want to disable this var name; if (state.opts.nostrip !== true) { name = "./" + _path3.default.join(src, _path3.default.basename(file)); } else { name = "./" + _path3.default.join(src, file); } // Special behavior if 'filterNames' if (filterNames.length > 0) { var _importDeclaration = t.importDeclaration([t.importDefaultSpecifier(t.identifier(fancyName))], t.stringLiteral(name)); path.insertAfter(_importDeclaration); continue; } // Generate temp. import declaration var importDeclaration = t.importDeclaration([isDir ? t.importNamespaceSpecifier(id) : t.importDefaultSpecifier(id)], t.stringLiteral(name)); // Assign it if (wildcardName !== null) { var thing = t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.identifier(wildcardName), t.stringLiteral(fancyName), true), id)); path.insertAfter(thing); } path.insertAfter(importDeclaration); } if (path.node.specifiers.length === 0) { path.remove(); } } } } }; }; var _path2 = require('path'); var _path3 = _interopRequireDefault(_path2); var _fs2 = require('fs'); var _fs3 = _interopRequireDefault(_fs2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }