UNPKG

babel-plugin-transform-import-meta

Version:
155 lines (150 loc) 9.08 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var template = require('@babel/template'); /** * Rewrites known `import.meta`[1] properties into equivalent non-module node.js * expressions. In order to maintain compatibility with plugins transforming * non-standard properties, this plugin transforms only known properties and * does not touch expressions with unknown or without member property access. * Properties known to this plugin: * * - `url`[2] * * [1]: https://github.com/tc39/proposal-import-meta * [2]: https://html.spec.whatwg.org/#hostgetimportmetaproperties */ function index () { return { name: 'transform-import-meta', visitor: { // eslint-disable-next-line complexity -- I don't know how to do it better Program(path, state) { var _a; const { module: target = 'CommonJS' } = (_a = state.opts) !== null && _a !== void 0 ? _a : {}; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- user input may not match type if (target !== 'CommonJS' && target !== 'ES6') { throw new Error('Invalid target, must be one of: "CommonJS" or "ES6"'); } const urlMetas = []; const filenameMetas = []; const dirnameMetas = []; const resolveMetas = []; const urlScopeIdentifiers = new Set(); const resolveScopeIdentifiers = new Set(); // Gather all of the relevant data for import.meta's that we're going to replace later. path.traverse({ // eslint-disable-next-line complexity -- I don't know how to do it better MemberExpression(memberExpPath) { const { node } = memberExpPath; if (node.object.type === 'MetaProperty' && node.object.meta.name === 'import' && node.object.property.name === 'meta' && node.property.type === 'Identifier' && node.property.name === 'url') { urlMetas.push(memberExpPath); for (const name of Object.keys(memberExpPath.scope.getAllBindings())) { urlScopeIdentifiers.add(name); } } if (node.object.type === 'MetaProperty' && node.object.meta.name === 'import' && node.object.property.name === 'meta' && node.property.type === 'Identifier' && node.property.name === 'filename') { filenameMetas.push(memberExpPath); } if (node.object.type === 'MetaProperty' && node.object.meta.name === 'import' && node.object.property.name === 'meta' && node.property.type === 'Identifier' && node.property.name === 'dirname') { dirnameMetas.push(memberExpPath); } }, CallExpression(callExpPath) { const { node } = callExpPath; if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'MetaProperty' && node.callee.object.meta.name === 'import' && node.callee.object.property.name === 'meta' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'resolve') { resolveMetas.push(callExpPath); for (const name of Object.keys(callExpPath.scope.getAllBindings())) { resolveScopeIdentifiers.add(name); } } }, OptionalCallExpression(callExpPath) { const { node } = callExpPath; if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'MetaProperty' && node.callee.object.meta.name === 'import' && node.callee.object.property.name === 'meta' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'resolve') { resolveMetas.push(callExpPath); for (const name of Object.keys(callExpPath.scope.getAllBindings())) { resolveScopeIdentifiers.add(name); } } } }); // For url and resolve, we'll potentially need to add imports, depending on the target. if ((urlMetas.length !== 0) || (resolveMetas.length !== 0)) { // eslint-disable-next-line @typescript-eslint/init-declarations -- no obvious default let metaUrlReplacement; // eslint-disable-next-line @typescript-eslint/init-declarations -- no obvious default let metaResolveReplacement; switch (target) { case 'CommonJS': { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting metaUrlReplacement = template.smart.ast `require('url').pathToFileURL(__filename).toString()`; // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting metaResolveReplacement = (args) => template.smart.ast `require('url').pathToFileURL(require.resolve(${args})).toString()`; break; } case 'ES6': { let urlId = 'url'; let createRequireId = 'createRequire'; while (urlScopeIdentifiers.has(urlId) || resolveScopeIdentifiers.has(urlId)) { urlId = path.scope.generateUidIdentifier('url').name; } while (resolveScopeIdentifiers.has(createRequireId)) { createRequireId = path.scope.generateUidIdentifier('createRequire').name; } // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting path.node.body.unshift(template.smart.ast `import ${urlId} from 'url';`); if (resolveMetas.length !== 0) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting path.node.body.unshift(template.smart.ast `import { createRequire as ${createRequireId} } from 'module';`); } // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting metaUrlReplacement = template.smart.ast `${urlId}.pathToFileURL(__filename).toString()`; // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting metaResolveReplacement = (args) => template.smart.ast `${urlId}.pathToFileURL(${createRequireId}(${urlId}.pathToFileURL(__filename).toString()).resolve(${args})).toString()`; break; } } for (const meta of urlMetas) { meta.replaceWith(metaUrlReplacement); } for (const meta of resolveMetas) { meta.replaceWith(metaResolveReplacement(meta.node.arguments)); } } // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting const metaFilenameReplacement = template.smart.ast `__filename`; // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- deterministic casting const metaDirnameReplacement = template.smart.ast `__dirname`; for (const meta of filenameMetas) { meta.replaceWith(metaFilenameReplacement); } for (const meta of dirnameMetas) { meta.replaceWith(metaDirnameReplacement); } } } }; } exports.default = index;