UNPKG

babel-plugin-codegen

Version:
155 lines (129 loc) 4.52 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.requireFromString = requireFromString; exports.getReplacement = getReplacement; exports.replace = replace; exports.resolveModuleContents = resolveModuleContents; exports.isCodegenComment = isCodegenComment; exports.isPropertyCall = isPropertyCall; exports.looksLike = looksLike; exports.getFilename = void 0; var _path = _interopRequireDefault(require("path")); var _fs = _interopRequireDefault(require("fs")); var _requireFromString = _interopRequireDefault(require("require-from-string")); // istanbul ignore next because I don't know how to reproduce a situation // where the filename doesn't exist, but TypeScript gets mad when I don't handle that case. const getFilename = fileOpts => { var _fileOpts$filename; return (_fileOpts$filename = fileOpts.filename) != null ? _fileOpts$filename : '"unknown"'; }; exports.getFilename = getFilename; function requireFromString(code, filename) { // Execute the transformed code, as if it were required const module = (0, _requireFromString.default)(String(code), filename); if (typeof module === 'string' || typeof module === 'function') { return module; } else { // Allow for es modules (default export) return module.__esModule ? module.default : module; } } function getReplacement({ code, fileOpts, args = [] }, babel) { const filename = getFilename(fileOpts); let module = requireFromString(code, filename); // If a function is epxorted, call it with args if (typeof module === 'function') { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument module = module(...args); } else if (args.length) { throw new Error(`codegen module (${_path.default.relative(process.cwd(), filename)}) cannot accept arguments because it does not export a function (it exports a ${typeof module}). You passed the arguments: ${args.join(', ')}`); } // Convert whatever we got now (hopefully a string) into AST form if (typeof module !== 'string') { throw new Error('codegen: Must module.exports a string.'); } return babel.template(module, { preserveComments: true, placeholderPattern: false, ...fileOpts.parserOpts, sourceType: 'module' })(); } function applyReplacementToPath(replacement, path) { if (replacement) { // If it's not an array, wrap into an array // to support single import/export declarations: // https://github.com/kentcdodds/babel-plugin-codegen/issues/30 path.replaceWithMultiple(Array.isArray(replacement) ? replacement : [replacement]); } else { path.remove(); } } function replace({ path, code, fileOpts, args }, babel) { const replacement = getReplacement({ code, args, fileOpts }, babel); applyReplacementToPath(replacement, path); } function resolveModuleContents({ filename, module }) { const resolvedPath = _path.default.resolve(_path.default.dirname(filename), module); const code = _fs.default.readFileSync(require.resolve(resolvedPath)); return { code, resolvedPath }; } function isCodegenComment(comment) { const normalisedComment = comment.value.trim().split(' ')[0].trim(); return normalisedComment.startsWith('codegen') || normalisedComment.startsWith('@codegen'); } function isPropertyCall(path, name) { return looksLike(path, { node: { type: 'CallExpression', callee: { property: { name } } } }); } // really difficult (impossible?) to make this work with explicit types // but if I could, I would make it this: // type LooksLikeTarget = Primitive | Function | {[key: string]: LooksLikeTarget} function looksLike(a, b) { if (isPrimitive(b)) return a === b; if (typeof b === 'function') return b(a); // istanbul ignore next because we don't have this use case // but if anyone copy/pastes this handy utility, they might need it! if (isPrimitive(a) || typeof a === 'function') return false; return Object.keys(b).every(bKey => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access return looksLike(a[bKey], b[bKey]); }); } function isPrimitive(val) { // eslint-disable-next-line return val == null || /^[sbn]/.test(typeof val); } /* eslint @typescript-eslint/no-explicit-any: "off", complexity: ["error", 8], import/no-unassigned-import: "off", import/no-dynamic-require: "off", */