@wessberg/cjs-to-esm-transformer
Version:
A Custom Transformer for Typescript that transforms Node-style CommonJS to tree-shakeable ES Modules
1,401 lines (1,384 loc) • 120 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var fs = require('fs');
var fs__default = _interopDefault(fs);
var TS = require('typescript');
var util = require('util');
var glob = require('glob');
var path = require('path');
var resolve = require('resolve');
var stringutil = require('@wessberg/stringutil');
var reservedWords = require('reserved-words');
const CONSTANT = {
inspectOptions: {
colors: true,
depth: Infinity,
maxArrayLength: Infinity
}
};
/**
* Returns true if the given path represents an external library
*
* @param path
* @returns
*/
function isExternalLibrary(path) {
return !path.startsWith(".") && !path.startsWith("/");
}
/**
* A map between id's and their results from previous resolving
* @type {Map<string, string|undefined>}
*/
const cache = new Map();
/**
* Computes a cache key based on the combination of id and parent
*
* @param id
* @param parent
* @return
*/
function computeCacheKey(id, parent) {
return isExternalLibrary(id) ? id : `${parent == null ? "" : `${parent}->`}${id}`;
}
/**
* A function that can resolve an import path
*
* @param options
* @returns
*/
function resolvePath({ id, parent, prioritizedPackageKeys = ["es2015", "esm2015", "module", "jsnext:main", "main", "browser"], prioritizedExtensions = ["", ".js", ".mjs", ".jsx", ".ts", ".tsx", ".json"], moduleDirectory = "node_modules", fileExists, readFile }) {
const cacheKey = computeCacheKey(id, parent);
// Attempt to take the resolve result from the cache
const cacheResult = cache.get(cacheKey);
// If it is a proper path, return it
if (cacheResult != null)
return cacheResult;
// Otherwise, if the cache result isn't strictly equal to 'undefined', it has previously been resolved to a non-existing file
if (cacheResult === null)
return;
if (!isExternalLibrary(id)) {
const absolute = path.isAbsolute(id) ? id : path.join(parent == null ? "" : path.dirname(parent), id);
const variants = [absolute, path.join(absolute, "index")];
for (const variant of variants) {
for (const ext of prioritizedExtensions) {
const withExtension = `${variant}${ext}`;
if (fileExists(withExtension)) {
// Add it to the cache
cache.set(cacheKey, withExtension);
return withExtension;
}
}
}
// Add it to the cache and mark it as unresolvable
cache.set(cacheKey, null);
return undefined;
}
// Otherwise, try to resolve it via node module resolution and put it in the cache
try {
const resolveResult = resolve.sync(id, {
extensions: prioritizedExtensions,
moduleDirectory,
readFileSync: (file, charset) => readFile(file, charset),
isFile: file => fileExists(file),
packageFilter(pkg) {
let property;
// Otherwise, or if no key was selected, use the prioritized list of fields and take the first matched one
if (property == null) {
const packageKeys = Object.keys(pkg);
property = prioritizedPackageKeys.find(key => packageKeys.includes(key));
}
// If a property was resolved, set the 'main' property to it (resolve will use the main property no matter what)
if (property != null) {
pkg.main = pkg[property];
}
// Return the package
return pkg;
}
});
// Add it to the cache
cache.set(cacheKey, resolveResult);
// Return it
return resolveResult;
}
catch (ex) {
// No file could be resolved. Set it in the cache as unresolvable and return void
cache.set(cacheKey, null);
// Return undefined¬
return undefined;
}
}
function walkThroughFillerNodes(expression, typescript) {
if (typescript.isParenthesizedExpression(expression) ||
typescript.isAsExpression(expression) ||
typescript.isTypeAssertion(expression) ||
typescript.isNonNullExpression(expression) ||
typescript.isTypeAssertion(expression) ||
typescript.isExpressionWithTypeArguments(expression)) {
return expression.expression;
}
return expression;
}
/* eslint-disable */
/**
* @file This file is auto-generated. Do not change its contents.
*/
const BUILT_IN_MODULE = new Set([
"assert",
"async_hooks",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"dns",
"domain",
"events",
"fs",
"fs/promises",
"http",
"http2",
"https",
"inspector",
"module",
"net",
"os",
"path",
"perf_hooks",
"process",
"punycode",
"querystring",
"readline",
"repl",
"stream",
"string_decoder",
"timers",
"tls",
"trace_events",
"tty",
"url",
"util",
"v8",
"vm",
"worker_threads",
"zlib"
]);
function isBuiltInModule(moduleName) {
return BUILT_IN_MODULE.has(moduleName);
}
const BUILT_IN_MODULE_MAP = {
assert: {
namedExports: new Set([]),
hasDefaultExport: true
},
async_hooks: {
namedExports: new Set(["AsyncLocalStorage", "createHook", "executionAsyncId", "triggerAsyncId", "executionAsyncResource", "AsyncResource"]),
hasDefaultExport: true
},
buffer: {
namedExports: new Set(["Buffer", "SlowBuffer", "transcode", "kMaxLength", "kStringMaxLength", "constants", "INSPECT_MAX_BYTES"]),
hasDefaultExport: true
},
child_process: {
namedExports: new Set(["ChildProcess", "exec", "execFile", "execFileSync", "execSync", "fork", "spawn", "spawnSync"]),
hasDefaultExport: true
},
cluster: {
namedExports: new Set(["isWorker", "isMaster", "Worker", "workers", "settings", "SCHED_NONE", "SCHED_RR", "schedulingPolicy", "setupMaster", "fork", "disconnect"]),
hasDefaultExport: true
},
console: {
namedExports: new Set([
"log",
"warn",
"dir",
"time",
"timeEnd",
"timeLog",
"trace",
"assert",
"clear",
"count",
"countReset",
"group",
"groupEnd",
"table",
"debug",
"info",
"dirxml",
"error",
"groupCollapsed",
"Console",
"profile",
"profileEnd",
"timeStamp",
"context"
]),
hasDefaultExport: true
},
constants: {
namedExports: new Set([
"RTLD_LAZY",
"RTLD_NOW",
"RTLD_GLOBAL",
"RTLD_LOCAL",
"E2BIG",
"EACCES",
"EADDRINUSE",
"EADDRNOTAVAIL",
"EAFNOSUPPORT",
"EAGAIN",
"EALREADY",
"EBADF",
"EBADMSG",
"EBUSY",
"ECANCELED",
"ECHILD",
"ECONNABORTED",
"ECONNREFUSED",
"ECONNRESET",
"EDEADLK",
"EDESTADDRREQ",
"EDOM",
"EDQUOT",
"EEXIST",
"EFAULT",
"EFBIG",
"EHOSTUNREACH",
"EIDRM",
"EILSEQ",
"EINPROGRESS",
"EINTR",
"EINVAL",
"EIO",
"EISCONN",
"EISDIR",
"ELOOP",
"EMFILE",
"EMLINK",
"EMSGSIZE",
"EMULTIHOP",
"ENAMETOOLONG",
"ENETDOWN",
"ENETRESET",
"ENETUNREACH",
"ENFILE",
"ENOBUFS",
"ENODATA",
"ENODEV",
"ENOENT",
"ENOEXEC",
"ENOLCK",
"ENOLINK",
"ENOMEM",
"ENOMSG",
"ENOPROTOOPT",
"ENOSPC",
"ENOSR",
"ENOSTR",
"ENOSYS",
"ENOTCONN",
"ENOTDIR",
"ENOTEMPTY",
"ENOTSOCK",
"ENOTSUP",
"ENOTTY",
"ENXIO",
"EOPNOTSUPP",
"EOVERFLOW",
"EPERM",
"EPIPE",
"EPROTO",
"EPROTONOSUPPORT",
"EPROTOTYPE",
"ERANGE",
"EROFS",
"ESPIPE",
"ESRCH",
"ESTALE",
"ETIME",
"ETIMEDOUT",
"ETXTBSY",
"EWOULDBLOCK",
"EXDEV",
"PRIORITY_LOW",
"PRIORITY_BELOW_NORMAL",
"PRIORITY_NORMAL",
"PRIORITY_ABOVE_NORMAL",
"PRIORITY_HIGH",
"PRIORITY_HIGHEST",
"SIGHUP",
"SIGINT",
"SIGQUIT",
"SIGILL",
"SIGTRAP",
"SIGABRT",
"SIGIOT",
"SIGBUS",
"SIGFPE",
"SIGKILL",
"SIGUSR1",
"SIGSEGV",
"SIGUSR2",
"SIGPIPE",
"SIGALRM",
"SIGTERM",
"SIGCHLD",
"SIGCONT",
"SIGSTOP",
"SIGTSTP",
"SIGTTIN",
"SIGTTOU",
"SIGURG",
"SIGXCPU",
"SIGXFSZ",
"SIGVTALRM",
"SIGPROF",
"SIGWINCH",
"SIGIO",
"SIGINFO",
"SIGSYS",
"UV_FS_SYMLINK_DIR",
"UV_FS_SYMLINK_JUNCTION",
"O_RDONLY",
"O_WRONLY",
"O_RDWR",
"UV_DIRENT_UNKNOWN",
"UV_DIRENT_FILE",
"UV_DIRENT_DIR",
"UV_DIRENT_LINK",
"UV_DIRENT_FIFO",
"UV_DIRENT_SOCKET",
"UV_DIRENT_CHAR",
"UV_DIRENT_BLOCK",
"S_IFMT",
"S_IFREG",
"S_IFDIR",
"S_IFCHR",
"S_IFBLK",
"S_IFIFO",
"S_IFLNK",
"S_IFSOCK",
"O_CREAT",
"O_EXCL",
"UV_FS_O_FILEMAP",
"O_NOCTTY",
"O_TRUNC",
"O_APPEND",
"O_DIRECTORY",
"O_NOFOLLOW",
"O_SYNC",
"O_DSYNC",
"O_SYMLINK",
"O_NONBLOCK",
"S_IRWXU",
"S_IRUSR",
"S_IWUSR",
"S_IXUSR",
"S_IRWXG",
"S_IRGRP",
"S_IWGRP",
"S_IXGRP",
"S_IRWXO",
"S_IROTH",
"S_IWOTH",
"S_IXOTH",
"F_OK",
"R_OK",
"W_OK",
"X_OK",
"UV_FS_COPYFILE_EXCL",
"COPYFILE_EXCL",
"UV_FS_COPYFILE_FICLONE",
"COPYFILE_FICLONE",
"UV_FS_COPYFILE_FICLONE_FORCE",
"COPYFILE_FICLONE_FORCE",
"OPENSSL_VERSION_NUMBER",
"SSL_OP_ALL",
"SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION",
"SSL_OP_CIPHER_SERVER_PREFERENCE",
"SSL_OP_CISCO_ANYCONNECT",
"SSL_OP_COOKIE_EXCHANGE",
"SSL_OP_CRYPTOPRO_TLSEXT_BUG",
"SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS",
"SSL_OP_EPHEMERAL_RSA",
"SSL_OP_LEGACY_SERVER_CONNECT",
"SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER",
"SSL_OP_MICROSOFT_SESS_ID_BUG",
"SSL_OP_MSIE_SSLV2_RSA_PADDING",
"SSL_OP_NETSCAPE_CA_DN_BUG",
"SSL_OP_NETSCAPE_CHALLENGE_BUG",
"SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG",
"SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG",
"SSL_OP_NO_COMPRESSION",
"SSL_OP_NO_QUERY_MTU",
"SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION",
"SSL_OP_NO_SSLv2",
"SSL_OP_NO_SSLv3",
"SSL_OP_NO_TICKET",
"SSL_OP_NO_TLSv1",
"SSL_OP_NO_TLSv1_1",
"SSL_OP_NO_TLSv1_2",
"SSL_OP_PKCS1_CHECK_1",
"SSL_OP_PKCS1_CHECK_2",
"SSL_OP_SINGLE_DH_USE",
"SSL_OP_SINGLE_ECDH_USE",
"SSL_OP_SSLEAY_080_CLIENT_DH_BUG",
"SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG",
"SSL_OP_TLS_BLOCK_PADDING_BUG",
"SSL_OP_TLS_D5_BUG",
"SSL_OP_TLS_ROLLBACK_BUG",
"ENGINE_METHOD_RSA",
"ENGINE_METHOD_DSA",
"ENGINE_METHOD_DH",
"ENGINE_METHOD_RAND",
"ENGINE_METHOD_EC",
"ENGINE_METHOD_CIPHERS",
"ENGINE_METHOD_DIGESTS",
"ENGINE_METHOD_PKEY_METHS",
"ENGINE_METHOD_PKEY_ASN1_METHS",
"ENGINE_METHOD_ALL",
"ENGINE_METHOD_NONE",
"DH_CHECK_P_NOT_SAFE_PRIME",
"DH_CHECK_P_NOT_PRIME",
"DH_UNABLE_TO_CHECK_GENERATOR",
"DH_NOT_SUITABLE_GENERATOR",
"ALPN_ENABLED",
"RSA_PKCS1_PADDING",
"RSA_SSLV23_PADDING",
"RSA_NO_PADDING",
"RSA_PKCS1_OAEP_PADDING",
"RSA_X931_PADDING",
"RSA_PKCS1_PSS_PADDING",
"RSA_PSS_SALTLEN_DIGEST",
"RSA_PSS_SALTLEN_MAX_SIGN",
"RSA_PSS_SALTLEN_AUTO",
"defaultCoreCipherList",
"TLS1_VERSION",
"TLS1_1_VERSION",
"TLS1_2_VERSION",
"TLS1_3_VERSION",
"POINT_CONVERSION_COMPRESSED",
"POINT_CONVERSION_UNCOMPRESSED",
"POINT_CONVERSION_HYBRID"
]),
hasDefaultExport: true
},
crypto: {
namedExports: new Set([
"createCipheriv",
"createDecipheriv",
"createDiffieHellman",
"createDiffieHellmanGroup",
"createECDH",
"createHash",
"createHmac",
"createPrivateKey",
"createPublicKey",
"createSecretKey",
"createSign",
"createVerify",
"diffieHellman",
"getCiphers",
"getCurves",
"getDiffieHellman",
"getHashes",
"pbkdf2",
"pbkdf2Sync",
"generateKeyPair",
"generateKeyPairSync",
"privateDecrypt",
"privateEncrypt",
"publicDecrypt",
"publicEncrypt",
"randomBytes",
"randomFill",
"randomFillSync",
"scrypt",
"scryptSync",
"sign",
"setEngine",
"timingSafeEqual",
"getFips",
"setFips",
"verify",
"Certificate",
"Cipher",
"Cipheriv",
"Decipher",
"Decipheriv",
"DiffieHellman",
"DiffieHellmanGroup",
"ECDH",
"Hash",
"Hmac",
"KeyObject",
"Sign",
"Verify",
"constants"
]),
hasDefaultExport: true
},
dgram: {
namedExports: new Set(["createSocket", "Socket"]),
hasDefaultExport: true
},
dns: {
namedExports: new Set([
"lookup",
"lookupService",
"Resolver",
"setServers",
"ADDRCONFIG",
"ALL",
"V4MAPPED",
"NODATA",
"FORMERR",
"SERVFAIL",
"NOTFOUND",
"NOTIMP",
"REFUSED",
"BADQUERY",
"BADNAME",
"BADFAMILY",
"BADRESP",
"CONNREFUSED",
"TIMEOUT",
"EOF",
"FILE",
"NOMEM",
"DESTRUCTION",
"BADSTR",
"BADFLAGS",
"NONAME",
"BADHINTS",
"NOTINITIALIZED",
"LOADIPHLPAPI",
"ADDRGETNETWORKPARAMS",
"CANCELLED",
"getServers",
"resolve",
"resolve4",
"resolve6",
"resolveAny",
"resolveCname",
"resolveMx",
"resolveNaptr",
"resolveNs",
"resolvePtr",
"resolveSoa",
"resolveSrv",
"resolveTxt",
"reverse",
"promises"
]),
hasDefaultExport: true
},
domain: {
namedExports: new Set(["Domain", "createDomain", "create", "active"]),
hasDefaultExport: true
},
events: {
namedExports: new Set([]),
hasDefaultExport: true
},
fs: {
namedExports: new Set([
"appendFile",
"appendFileSync",
"access",
"accessSync",
"chown",
"chownSync",
"chmod",
"chmodSync",
"close",
"closeSync",
"copyFile",
"copyFileSync",
"createReadStream",
"createWriteStream",
"exists",
"existsSync",
"fchown",
"fchownSync",
"fchmod",
"fchmodSync",
"fdatasync",
"fdatasyncSync",
"fstat",
"fstatSync",
"fsync",
"fsyncSync",
"ftruncate",
"ftruncateSync",
"futimes",
"futimesSync",
"lchown",
"lchownSync",
"lchmod",
"lchmodSync",
"link",
"linkSync",
"lstat",
"lstatSync",
"mkdir",
"mkdirSync",
"mkdtemp",
"mkdtempSync",
"open",
"openSync",
"opendir",
"opendirSync",
"readdir",
"readdirSync",
"read",
"readSync",
"readv",
"readvSync",
"readFile",
"readFileSync",
"readlink",
"readlinkSync",
"realpath",
"realpathSync",
"rename",
"renameSync",
"rmdir",
"rmdirSync",
"stat",
"statSync",
"symlink",
"symlinkSync",
"truncate",
"truncateSync",
"unwatchFile",
"unlink",
"unlinkSync",
"utimes",
"utimesSync",
"watch",
"watchFile",
"writeFile",
"writeFileSync",
"write",
"writeSync",
"writev",
"writevSync",
"Dir",
"Dirent",
"Stats",
"ReadStream",
"WriteStream",
"FileReadStream",
"FileWriteStream",
"F_OK",
"R_OK",
"W_OK",
"X_OK",
"constants",
"promises"
]),
hasDefaultExport: true
},
"fs/promises": {
namedExports: new Set([
"access",
"copyFile",
"open",
"opendir",
"rename",
"truncate",
"rmdir",
"mkdir",
"readdir",
"readlink",
"symlink",
"lstat",
"stat",
"link",
"unlink",
"chmod",
"lchmod",
"lchown",
"chown",
"utimes",
"realpath",
"mkdtemp",
"writeFile",
"appendFile",
"readFile"
]),
hasDefaultExport: true
},
http: {
namedExports: new Set([
"METHODS",
"STATUS_CODES",
"Agent",
"ClientRequest",
"IncomingMessage",
"OutgoingMessage",
"Server",
"ServerResponse",
"createServer",
"validateHeaderName",
"validateHeaderValue",
"get",
"request",
"maxHeaderSize",
"globalAgent"
]),
hasDefaultExport: true
},
http2: {
namedExports: new Set([
"connect",
"constants",
"createServer",
"createSecureServer",
"getDefaultSettings",
"getPackedSettings",
"getUnpackedSettings",
"Http2ServerRequest",
"Http2ServerResponse"
]),
hasDefaultExport: true
},
https: {
namedExports: new Set(["Agent", "globalAgent", "Server", "createServer", "get", "request"]),
hasDefaultExport: true
},
inspector: {
namedExports: new Set(["open", "close", "url", "waitForDebugger", "console", "Session"]),
hasDefaultExport: true
},
module: {
namedExports: new Set([]),
hasDefaultExport: true
},
net: {
namedExports: new Set(["connect", "createConnection", "createServer", "isIP", "isIPv4", "isIPv6", "Server", "Socket", "Stream"]),
hasDefaultExport: true
},
os: {
namedExports: new Set([
"arch",
"cpus",
"endianness",
"freemem",
"getPriority",
"homedir",
"hostname",
"loadavg",
"networkInterfaces",
"platform",
"release",
"setPriority",
"tmpdir",
"totalmem",
"type",
"userInfo",
"uptime",
"version",
"constants",
"EOL"
]),
hasDefaultExport: true
},
path: {
namedExports: new Set([
"resolve",
"normalize",
"isAbsolute",
"join",
"relative",
"toNamespacedPath",
"dirname",
"basename",
"extname",
"format",
"parse",
"sep",
"delimiter",
"win32",
"posix"
]),
hasDefaultExport: true
},
perf_hooks: {
namedExports: new Set(["performance", "PerformanceObserver", "monitorEventLoopDelay", "constants"]),
hasDefaultExport: true
},
process: {
namedExports: new Set([
"version",
"versions",
"arch",
"platform",
"release",
"moduleLoadList",
"binding",
"domain",
"config",
"dlopen",
"uptime",
"reallyExit",
"hrtime",
"cpuUsage",
"resourceUsage",
"memoryUsage",
"kill",
"exit",
"openStdin",
"getuid",
"geteuid",
"getgid",
"getegid",
"getgroups",
"allowedNodeEnvironmentFlags",
"assert",
"features",
"setUncaughtExceptionCaptureCallback",
"hasUncaughtExceptionCaptureCallback",
"emitWarning",
"nextTick",
"stdout",
"stdin",
"stderr",
"abort",
"umask",
"chdir",
"cwd",
"initgroups",
"setgroups",
"setegid",
"seteuid",
"setgid",
"setuid",
"env",
"title",
"argv",
"execArgv",
"pid",
"ppid",
"execPath",
"debugPort",
"argv0",
"mainModule",
"emit"
]),
hasDefaultExport: true
},
punycode: {
namedExports: new Set(["version", "ucs2", "decode", "encode", "toASCII", "toUnicode"]),
hasDefaultExport: true
},
querystring: {
namedExports: new Set(["unescapeBuffer", "unescape", "escape", "stringify", "encode", "parse", "decode"]),
hasDefaultExport: true
},
readline: {
namedExports: new Set(["Interface", "clearLine", "clearScreenDown", "createInterface", "cursorTo", "emitKeypressEvents", "moveCursor"]),
hasDefaultExport: true
},
repl: {
namedExports: new Set(["start", "writer", "REPLServer", "REPL_MODE_SLOPPY", "REPL_MODE_STRICT", "Recoverable"]),
hasDefaultExport: true
},
stream: {
namedExports: new Set([]),
hasDefaultExport: true
},
string_decoder: {
namedExports: new Set(["StringDecoder"]),
hasDefaultExport: true
},
timers: {
namedExports: new Set(["setTimeout", "clearTimeout", "setImmediate", "clearImmediate", "setInterval", "clearInterval", "active", "unenroll", "enroll"]),
hasDefaultExport: true
},
tls: {
namedExports: new Set([
"CLIENT_RENEG_LIMIT",
"CLIENT_RENEG_WINDOW",
"DEFAULT_CIPHERS",
"DEFAULT_ECDH_CURVE",
"DEFAULT_MIN_VERSION",
"DEFAULT_MAX_VERSION",
"getCiphers",
"rootCertificates",
"convertALPNProtocols",
"checkServerIdentity",
"parseCertString",
"createSecureContext",
"SecureContext",
"TLSSocket",
"Server",
"createServer",
"connect",
"createSecurePair"
]),
hasDefaultExport: true
},
trace_events: {
namedExports: new Set(["createTracing", "getEnabledCategories"]),
hasDefaultExport: true
},
tty: {
namedExports: new Set(["isatty", "ReadStream", "WriteStream"]),
hasDefaultExport: true
},
url: {
namedExports: new Set(["Url", "parse", "resolve", "resolveObject", "format", "URL", "URLSearchParams", "domainToASCII", "domainToUnicode", "pathToFileURL", "fileURLToPath"]),
hasDefaultExport: true
},
util: {
namedExports: new Set([
"callbackify",
"debuglog",
"deprecate",
"format",
"formatWithOptions",
"getSystemErrorName",
"inherits",
"inspect",
"isArray",
"isBoolean",
"isBuffer",
"isDeepStrictEqual",
"isNull",
"isNullOrUndefined",
"isNumber",
"isString",
"isSymbol",
"isUndefined",
"isRegExp",
"isObject",
"isDate",
"isError",
"isFunction",
"isPrimitive",
"log",
"promisify",
"TextDecoder",
"TextEncoder",
"types"
]),
hasDefaultExport: true
},
v8: {
namedExports: new Set([
"cachedDataVersionTag",
"getHeapSnapshot",
"getHeapStatistics",
"getHeapSpaceStatistics",
"getHeapCodeStatistics",
"setFlagsFromString",
"Serializer",
"Deserializer",
"DefaultSerializer",
"DefaultDeserializer",
"deserialize",
"serialize",
"writeHeapSnapshot"
]),
hasDefaultExport: true
},
vm: {
namedExports: new Set(["Script", "createContext", "createScript", "runInContext", "runInNewContext", "runInThisContext", "isContext", "compileFunction", "measureMemory"]),
hasDefaultExport: true
},
worker_threads: {
namedExports: new Set([
"isMainThread",
"MessagePort",
"MessageChannel",
"moveMessagePortToContext",
"receiveMessageOnPort",
"resourceLimits",
"threadId",
"SHARE_ENV",
"Worker",
"parentPort",
"workerData"
]),
hasDefaultExport: true
},
zlib: {
namedExports: new Set([
"Deflate",
"Inflate",
"Gzip",
"Gunzip",
"DeflateRaw",
"InflateRaw",
"Unzip",
"BrotliCompress",
"BrotliDecompress",
"deflate",
"deflateSync",
"gzip",
"gzipSync",
"deflateRaw",
"deflateRawSync",
"unzip",
"unzipSync",
"inflate",
"inflateSync",
"gunzip",
"gunzipSync",
"inflateRaw",
"inflateRawSync",
"brotliCompress",
"brotliCompressSync",
"brotliDecompress",
"brotliDecompressSync",
"createDeflate",
"createInflate",
"createDeflateRaw",
"createInflateRaw",
"createGzip",
"createGunzip",
"createUnzip",
"createBrotliCompress",
"createBrotliDecompress",
"constants",
"codes"
]),
hasDefaultExport: true
}
};
/**
* Checks if the CallExpression represents a require call (e.g.: 'require(...)')
*/
function isRequireCall(inputExpression, sourceFile, context) {
const { typescript } = context;
const callExpression = walkThroughFillerNodes(inputExpression, typescript);
if (!typescript.isCallExpression(callExpression))
return { match: false };
const expression = walkThroughFillerNodes(callExpression.expression, typescript);
if (!typescript.isIdentifier(expression) || expression.text !== "require")
return { match: false };
// Take the first argument, if there is any
const [firstArgument] = callExpression.arguments;
if (firstArgument == null)
return { match: false };
const moduleSpecifier = typescript.isStringLiteralLike(firstArgument) ? firstArgument.text : undefined;
const resolvedModuleSpecifier = moduleSpecifier == null
? undefined
: resolvePath({
id: moduleSpecifier,
parent: path.normalize(sourceFile.fileName),
readFile: context.readFile,
fileExists: context.fileExists
});
const resolvedModuleSpecifierText = resolvedModuleSpecifier == null || isBuiltInModule(resolvedModuleSpecifier) ? undefined : context.readFile(resolvedModuleSpecifier);
if (moduleSpecifier == null || resolvedModuleSpecifier == null || resolvedModuleSpecifierText == null) {
return {
match: true,
moduleSpecifier,
resolvedModuleSpecifier: undefined,
resolvedModuleSpecifierText: undefined
};
}
else {
return {
match: true,
moduleSpecifier,
resolvedModuleSpecifier: path.normalize(resolvedModuleSpecifier),
resolvedModuleSpecifierText
};
}
}
function findNodeUp(from, nodeCb, breakWhen) {
let current = from;
while (current.parent != null) {
current = current.parent;
if (breakWhen != null && breakWhen(current))
return undefined;
if (nodeCb(current))
return current;
}
return undefined;
}
/**
* Returns true if the given Node is a Statement
* Uses an internal non-exposed Typescript helper to decide whether or not the Node is an Expression
*/
function isStatement(node, typescript) {
return typescript.isStatementButNotDeclaration(node);
}
/**
* Returns true if the given Node is a Declaration
* Uses an internal non-exposed Typescript helper to decide whether or not the Node is an Expression
*/
function isDeclaration(node, typescript) {
return typescript.isDeclaration(node);
}
/**
* Returns true if the given Node is a Statement is a Declaration
*/
function isStatementOrDeclaration(node, typescript) {
return isStatement(node, typescript) || isDeclaration(node, typescript);
}
/**
* Generates a proper name based on the given module specifier
*
* @param moduleSpecifier
* @return
*/
function generateNameFromModuleSpecifier(moduleSpecifier) {
const { name } = path.parse(moduleSpecifier);
return stringutil.camelCase(name);
}
/**
* Tries to get or potentially parse module exports based on the given data in the given context
* @param data
* @param context
*/
function getModuleExportsFromRequireDataInContext(data, context) {
if (!data.match)
return undefined;
const { typescript } = context;
// Otherwise, spread out the things we know about the require call
const { moduleSpecifier, resolvedModuleSpecifierText, resolvedModuleSpecifier } = data;
// If no module specifier could be determined, remove the CallExpression from the SourceFile
if (moduleSpecifier == null) {
return undefined;
}
// If we've been able to resolve a module as well as its contents,
// Check it for exports so that we know more about its internals, for example whether or not it has any named exports, etc
let moduleExports;
// If no module specifier could be resolved, it may be a built in module - an we may know about its module exports already
if (resolvedModuleSpecifier == null && isBuiltInModule(moduleSpecifier)) {
moduleExports = BUILT_IN_MODULE_MAP[moduleSpecifier];
}
// Otherwise, if we could resolve a module, try to get the exports for it
else if (resolvedModuleSpecifier != null) {
// Try to get the ModuleExports for the resolved module, if we know them already
moduleExports = context.getModuleExportsForPath(resolvedModuleSpecifier);
// If that wasn't possible, generate a new SourceFile and parse it
if (moduleExports == null && resolvedModuleSpecifierText != null) {
moduleExports = transformSourceFile(typescript.createSourceFile(resolvedModuleSpecifier, resolvedModuleSpecifierText, typescript.ScriptTarget.ESNext, true, typescript.ScriptKind.TS), {
baseVisitorContext: {
...context,
onlyExports: true
}
}, context.transformationContext).exports;
}
}
return moduleExports;
}
function shouldDebug(debug, sourceFile) {
if (debug == null)
return false;
if (typeof debug === "boolean")
return debug;
if (sourceFile == null)
return true;
if (typeof debug === "string")
return sourceFile.fileName === debug;
else
return debug(sourceFile.fileName);
}
/**
* Visits the given CallExpression
*
* @param options
* @returns
*/
function visitCallExpression({ node, childContinuation, sourceFile, context }) {
if (context.onlyExports) {
return childContinuation(node);
}
// Check if the node represents a require(...) call.
const requireData = isRequireCall(node, sourceFile, context);
const { typescript } = context;
// If it doesn't proceed without applying any transformations
if (!requireData.match) {
return childContinuation(node);
}
// Otherwise, spread out the things we know about the require call
const { moduleSpecifier } = requireData;
// If no module specifier could be determined, remove the CallExpression from the SourceFile
if (moduleSpecifier == null) {
return undefined;
}
// If we've been able to resolve a module as well as its contents,
// Check it for exports so that we know more about its internals, for example whether or not it has any named exports, etc
const moduleExports = getModuleExportsFromRequireDataInContext(requireData, context);
// Find the first ExpressionStatement going up from the Node, breaking if part of a BinaryExpression, CallExpression, or a NewExpression
const expressionStatementParent = findNodeUp(node, typescript.isExpressionStatement, currentNode => typescript.isBinaryExpression(currentNode) || typescript.isCallExpression(currentNode) || typescript.isNewExpression(currentNode));
// If we don't know anything about the exports of the module, or if it doesn't export any named exports,
// there's really not much we can do in terms of using the context of the CallExpression to import the maximally
// minimal subset of the module. In these cases, the only thing that can be done is to import the default
// export and maybe return an identifier for it depending on whether or not the CallExpression is part of an ExpressionStatement
if (moduleExports == null || moduleExports.namedExports.size === 0 || (expressionStatementParent != null && !moduleExports.hasDefaultExport)) {
// If part of an ExpressionStatement, simply return the module without any name or other bindings
if (expressionStatementParent != null) {
// Only add the import if there isn't already an import within the SourceFile of the entire module without any bindings
if (!context.isModuleSpecifierImportedWithoutLocals(moduleSpecifier)) {
context.addImport(typescript.createImportDeclaration(undefined, undefined, undefined, typescript.createStringLiteral(moduleSpecifier)));
}
// Drop this CallExpression
return undefined;
}
// Otherwise, we need to give the module a name and replace the CallExpression with an identifier for it
else {
// If the default export is already imported, get the local binding name for it and create an identifier for it
// rather than generating a new unnecessary import
if (context.hasLocalForDefaultImportFromModule(moduleSpecifier)) {
const local = context.getLocalForDefaultImportFromModule(moduleSpecifier);
return typescript.createIdentifier(local);
}
else {
const identifier = typescript.createIdentifier(context.getFreeIdentifier(generateNameFromModuleSpecifier(moduleSpecifier)));
context.addImport(typescript.createImportDeclaration(undefined, undefined, typescript.createImportClause(identifier, undefined), typescript.createStringLiteral(moduleSpecifier)));
// Replace the CallExpression by the identifier
return identifier;
}
}
}
// Otherwise, we know that we want to add an import instead of the CallExpression, but depending on the context of the CallExpression, we may
// or may not import specific Named Exports, the Default Export, or the entire namespace.
// Find the first Element- or PropertyAccessExpression that wraps the require(...) call, whatever it is.
// That means that if it is wrapped in 'require(...)["foo"].bar', then the ElementAccessExpression will be matched first
const elementOrPropertyAccessExpressionParent = findNodeUp(node, child => typescript.isElementAccessExpression(child) || typescript.isPropertyAccessExpression(child), nextNode => isStatementOrDeclaration(nextNode, typescript));
if (elementOrPropertyAccessExpressionParent != null) {
// Try to evaluate the name or argument expression, depending on the kind of node
let rightValue;
// If it is a PropertyAccessExpression, the name will always be an identifier
if (typescript.isPropertyAccessExpression(elementOrPropertyAccessExpressionParent)) {
rightValue = elementOrPropertyAccessExpressionParent.name.text;
}
else {
// Otherwise, the argument may be any kind of expression. Try to evaluate it to a string literal if possible
if (typescript.isStringLiteralLike(elementOrPropertyAccessExpressionParent.argumentExpression)) {
rightValue = elementOrPropertyAccessExpressionParent.argumentExpression.text;
}
}
// The argumentExpression or name matched a string, use that as a candidate for a lookup binding
if (rightValue != null) {
// If the module doesn't include a named export with a name matching the right value,
// we should instead import the default export if it has any (otherwise we'll use a Namespace import) and replace the CallExpression with an identifier for it
if (!moduleExports.namedExports.has(rightValue)) {
let identifier;
// If the default export is already imported, get the local binding name for it and create an identifier for it
// rather than generating a new unnecessary import
if (moduleExports.hasDefaultExport && context.hasLocalForDefaultImportFromModule(moduleSpecifier)) {
identifier = typescript.createIdentifier(context.getLocalForDefaultImportFromModule(moduleSpecifier));
}
// If the namespace is already imported, get the local binding name for it and create an identifier for it
// rather than generating a new unnecessary import
else if (!moduleExports.hasDefaultExport && context.hasLocalForNamespaceImportFromModule(moduleSpecifier)) {
identifier = typescript.createIdentifier(context.getLocalForNamespaceImportFromModule(moduleSpecifier));
}
else {
identifier = typescript.createIdentifier(context.getFreeIdentifier(generateNameFromModuleSpecifier(moduleSpecifier)));
context.addImport(typescript.createImportDeclaration(undefined, undefined, moduleExports.hasDefaultExport
? // Import the default if it has any (or if we don't know if it has)
typescript.createImportClause(identifier, undefined)
: // Otherwise, import the entire namespace
typescript.createImportClause(undefined, typescript.createNamespaceImport(identifier)), typescript.createStringLiteral(moduleSpecifier)));
}
// Replace the CallExpression by an ObjectLiteral that can be accessed by the wrapping Element- or PropertyAccessExpression
return typescript.createObjectLiteral([
identifier.text !== rightValue
? typescript.createPropertyAssignment(rightValue, typescript.createIdentifier(identifier.text))
: typescript.createShorthandPropertyAssignment(typescript.createIdentifier(identifier.text))
]);
}
// Otherwise, use the right value as the ImportSpecifier for a new import.
// Depending on the placement of the CallExpression, we may or may not need to
// replace it with an identifier or remove it entirely in favor of the ImportDeclaration
else {
// The property to import will be equal to the right value
const importBindingPropertyName = rightValue;
let importBindingName;
// If the default export is already imported, get the local binding name for it and create an identifier for it
// rather than generating a new unnecessary import
if (context.hasLocalForNamedImportPropertyNameFromModule(importBindingPropertyName, moduleSpecifier)) {
importBindingName = context.getLocalForNamedImportPropertyNameFromModule(importBindingPropertyName, moduleSpecifier);
}
// If the namespace is already imported, get the local binding name for it and create an identifier for it
// rather than generating a new unnecessary import
else if (!moduleExports.hasDefaultExport && context.hasLocalForNamespaceImportFromModule(moduleSpecifier)) {
importBindingName = context.getLocalForNamespaceImportFromModule(moduleSpecifier);
}
else {
// If that binding isn't free within the context, import it as another local name
importBindingName = context.isIdentifierFree(importBindingPropertyName) ? importBindingPropertyName : context.getFreeIdentifier(importBindingPropertyName);
context.addImport(typescript.createImportDeclaration(undefined, undefined, typescript.createImportClause(undefined, typescript.createNamedImports([
importBindingPropertyName === importBindingName
? // If the property name is free within the context, don't alias the import
typescript.createImportSpecifier(undefined, typescript.createIdentifier(importBindingPropertyName))
: // Otherwise, import it aliased by another name that is free within the context
typescript.createImportSpecifier(typescript.createIdentifier(importBindingPropertyName), typescript.createIdentifier(importBindingName))
])), typescript.createStringLiteral(moduleSpecifier)));
}
// If the 'require(...)[<something>]' or 'require(...).<something>' expression is part of an ExpressionStatement
// and isn't part of another expression such as a BinaryExpression, only preserve the import.
// Otherwise leave an ObjectLiteral that can be accessed by the wrapping Element- or PropertyAccessExpression
if (expressionStatementParent == null) {
return typescript.createObjectLiteral([
importBindingName !== rightValue
? typescript.createPropertyAssignment(rightValue, typescript.createIdentifier(importBindingName))
: typescript.createShorthandPropertyAssignment(typescript.createIdentifier(importBindingName))
]);
}
else {
return undefined;
}
}
}
}
// If no lookup binding candidate has been determined, it may be determined based on the parent VariableDeclaration,
// if there is any.
// Find the first VariableDeclaration that holds the require(...) call, if any.
// For example, 'const foo = require(...)' would match the VariableDeclaration for 'foo'
const variableDeclarationParent = findNodeUp(node, typescript.isVariableDeclaration, nextNode => isStatement(nextNode, typescript));
if (variableDeclarationParent != null) {
// If the VariableDeclaration is simply bound to a name, it doesn't tell us anything interesting.
// Simply add an import for the default export - if it has any (otherwise we'll import the entire namespace), and
// replace this CallExpression by an identifier for it
if (typescript.isIdentifier(variableDeclarationParent.name)) {
// If the default export is already imported, get the local binding name for it and create an identifier for it
// rather than generating a new unnecessary import
if (moduleExports.hasDefaultExport && context.hasLocalForDefaultImportFromModule(moduleSpecifier)) {
const local = context.getLocalForDefaultImportFromModule(moduleSpecifier);
return typescript.createIdentifier(local);
}
// If the namespace is already imported, get the local binding name for it and create an identifier for it
// rather than generating a new unnecessary import
else if (!moduleExports.hasDefaultExport && context.hasLocalForNamespaceImportFromModule(moduleSpecifier)) {
const local = context.getLocalForNamespaceImportFromModule(moduleSpecifier);
return typescript.createIdentifier(local);
}
// Otherwise proceed as planned
else {
const identifier = typescript.createIdentifier(context.getFreeIdentifier(generateNameFromModuleSpecifier(moduleSpecifier)));
context.addImport(typescript.createImportDeclaration(undefined, undefined, moduleExports.hasDefaultExport
? // Import the default if it has any (or if we don't know if it has)
typescript.createImportClause(identifier, undefined)
: // Otherwise, import the entire namespace
typescript.createImportClause(undefined, typescript.createNamespaceImport(identifier)), typescript.createStringLiteral(moduleSpecifier)));
return identifier;
}
}
// If the VariableDeclaration is a BindingPattern, it may mimic destructuring specific named exports.
// For example, 'const {foo, bar} = require("./bar")' could import the named export bindings 'foo' and 'bar' from the module './bar'.
// However, if as much as a single one of these elements don't directly match a named export, opt out of this behavior and instead
// import the default export (if it has any, otherwise import the entire namespace).
else if (typescript.isObjectBindingPattern(variableDeclarationParent.name)) {
const importSpecifiers = [];
const skippedImportSpecifiers = [];
// Check each of the BindingElements
for (const element of variableDeclarationParent.name.elements) {
// If the property name isn't given, the name will always be an Identifier
if (element.propertyName == null && typescript.isIdentifier(element.name)) {
// If the module exports contains a named export matching the identifier name,
// use that as an ImportSpecifier
if (moduleExports.namedExports.has(element.name.text)) {
// If the property has already been imported, don't add an import, but instead push to 'skippedImportSpecifiers'.