module-from-string
Advanced tools
Comparing version 3.1.4 to 3.2.0-0
import path from 'path' | ||
import { importFromString, importFromStringSync } from '../../src/index' | ||
describe('importFromString', () => { | ||
let importFromStringFn: typeof importFromString | typeof importFromStringSync | ||
const testImport = (): void => { | ||
it('should work with named export', async () => { | ||
const res = await importFromString("export const greet = 'hi'") | ||
const res = await importFromStringFn("export const greet = 'hi'") | ||
expect(res.greet).toBe('hi') | ||
@@ -11,3 +13,3 @@ }) | ||
it('should work with default export', async () => { | ||
const res = await importFromString("export default 'hi'") | ||
const res = await importFromStringFn("export default 'hi'") | ||
expect(res.default).toBe('hi') | ||
@@ -18,3 +20,3 @@ }) | ||
const modulePath = './fixtures/namedExport.js' | ||
const res = await importFromString(`export { greet } from '${modulePath}'`) | ||
const res = await importFromStringFn(`export { greet } from '${modulePath}'`) | ||
expect(res.greet).toBe('hi') | ||
@@ -25,3 +27,3 @@ }) | ||
const modulePath = './cjs/fixtures/defaultExport.js' | ||
const res = await importFromString(`import greet from '${modulePath}'; export default greet`, { | ||
const res = await importFromStringFn(`import greet from '${modulePath}';export default greet`, { | ||
dirname: path.dirname(__dirname) | ||
@@ -34,3 +36,3 @@ }) | ||
const modulePath = path.join(__dirname, 'fixtures/namedExport.js') | ||
const res = await importFromString(`export { greet } from '${modulePath}'`) | ||
const res = await importFromStringFn(`export { greet } from '${modulePath}'`) | ||
expect(res.greet).toBe('hi') | ||
@@ -44,3 +46,3 @@ }) | ||
` | ||
const res = await importFromString(code) | ||
const res = await importFromStringFn(code) | ||
expect(res.default).toMatchInlineSnapshot(` | ||
@@ -55,62 +57,55 @@ "var Greet = /* @__PURE__ */ ((Greet2) => { | ||
it('should work if transformOption is provided', async () => { | ||
const res = await importFromString("export const greet: () => string = () => 'hi'", { | ||
transformOptions: { loader: 'ts' } | ||
}) | ||
expect(res.greet()).toBe('hi') | ||
it('should be able to access __dirname and __filename', () => { | ||
const res = importFromStringSync(` | ||
export const dirname = __dirname | ||
export const filename = __filename | ||
`) | ||
expect(res.dirname).toBe(__dirname) | ||
expect(res.filename).toMatch(__dirname) | ||
}) | ||
}) | ||
describe('importFromStringSync', () => { | ||
it('should work with named export', () => { | ||
const res = importFromStringSync("export const greet = 'hi'") | ||
it('should have access the global object', async () => { | ||
const res = await importFromStringFn('export const { greet } = global', { | ||
globals: { greet: 'hi' } | ||
}) | ||
expect(res.greet).toBe('hi') | ||
}) | ||
it('should work with default export', () => { | ||
const res = importFromStringSync("export default 'hi'") | ||
expect(res.default).toBe('hi') | ||
it('should work with current global', async () => { | ||
const res = await importFromStringFn('export default new Error()', { | ||
useCurrentGlobal: true | ||
}) | ||
expect(res.default).toBeInstanceOf(Error) | ||
}) | ||
it('should work with relative path import', () => { | ||
const modulePath = './fixtures/namedExport.js' | ||
const res = importFromStringSync(`export { greet } from '${modulePath}'`) | ||
expect(res.greet).toBe('hi') | ||
}) | ||
it('should resolve correctly if option `dirname` is provided', () => { | ||
const modulePath = './cjs/fixtures/defaultExport.js' | ||
const res = importFromStringSync(`import greet from '${modulePath}'; export default greet`, { | ||
dirname: path.dirname(__dirname) | ||
it('should work if transformOption is provided', async () => { | ||
const res = await importFromStringFn("export const greet: () => string = () => 'hi'", { | ||
transformOptions: { loader: 'ts' } | ||
}) | ||
expect(res.default).toBe('hi') | ||
expect(res.greet()).toBe('hi') | ||
}) | ||
it('should work with absolute path import', () => { | ||
const modulePath = path.join(__dirname, 'fixtures/namedExport.js') | ||
const res = importFromStringSync(`export { greet } from '${modulePath}'`) | ||
expect(res.greet).toBe('hi') | ||
it('should be able to override default shims', async () => { | ||
const relativeModulePath = './fixtures/namedExport.js' | ||
const modulePath = path.join(__dirname, relativeModulePath) | ||
const res = await importFromStringFn( | ||
`export default import.meta.resolve("${relativeModulePath}")`, | ||
{ | ||
transformOptions: { | ||
banner: 'var import_meta_resolve = require.resolve;' | ||
} | ||
} | ||
) | ||
expect(res.default).toBe(modulePath) | ||
}) | ||
} | ||
it('should work with import external module', () => { | ||
const code = `import { transformSync } from 'esbuild' | ||
const { code } = transformSync('enum Greet { Hi }', { loader: 'ts' }) | ||
export default code | ||
` | ||
const res = importFromStringSync(code) | ||
expect(res.default).toMatchInlineSnapshot(` | ||
"var Greet = /* @__PURE__ */ ((Greet2) => { | ||
Greet2[Greet2[\\"Hi\\"] = 0] = \\"Hi\\"; | ||
return Greet2; | ||
})(Greet || {}); | ||
" | ||
`) | ||
}) | ||
describe('importFromString', () => { | ||
importFromStringFn = importFromString | ||
testImport() | ||
}) | ||
it('should work if transformOption is provided', () => { | ||
const res = importFromStringSync("export const greet: () => string = () => 'hi'", { | ||
transformOptions: { loader: 'ts' } | ||
}) | ||
expect(res.greet()).toBe('hi') | ||
}) | ||
describe('importFromStringSync', () => { | ||
importFromStringFn = importFromStringSync | ||
testImport() | ||
}) |
@@ -15,3 +15,3 @@ import path from 'path' | ||
it('should work with relative path import', () => { | ||
it('should work with relative path require', () => { | ||
const modulePath = './fixtures/defaultExport.js' | ||
@@ -30,3 +30,3 @@ const res = requireFromString(`module.exports = require('${modulePath}')`) | ||
it('should work with absolute path import', () => { | ||
it('should work with absolute path require', () => { | ||
const modulePath = path.join(__dirname, 'fixtures/defaultExport.js') | ||
@@ -58,1 +58,15 @@ const res = requireFromString(`module.exports = require('${modulePath}')`) | ||
}) | ||
it('should have access the global object', () => { | ||
const res = requireFromString('module.exports = global.hi', { | ||
globals: { hi: 'hi' } | ||
}) | ||
expect(res).toBe('hi') | ||
}) | ||
it('should work with current global', () => { | ||
const res = requireFromString('module.exports = new Error()', { | ||
useCurrentGlobal: true | ||
}) | ||
expect(res).toBeInstanceOf(Error) | ||
}) |
@@ -5,3 +5,3 @@ import path from 'path' | ||
const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))) | ||
const __dirname = path.dirname(fileURLToPath(import.meta.url)) | ||
@@ -60,2 +60,30 @@ describe('importFromString', () => { | ||
it('should be able to access __dirname and __filename', () => { | ||
const res = importFromStringSync(` | ||
export const dirname = __dirname | ||
export const filename = __filename | ||
`) | ||
expect(res.dirname).toBe(__dirname) | ||
expect(res.filename).toMatch(__dirname) | ||
}) | ||
it('should be able to access import.meta.url', async () => { | ||
const res = await importFromString('export const { url } = import.meta') | ||
expect(res.url).toMatch(`file://${__dirname}`) | ||
}) | ||
it('should have access the global object', async () => { | ||
const res = await importFromString('export const { greet } = global', { | ||
globals: { greet: 'hi' } | ||
}) | ||
expect(res.greet).toBe('hi') | ||
}) | ||
it('should work with current global', async () => { | ||
const res = await importFromString('export default new Error()', { | ||
useCurrentGlobal: true | ||
}) | ||
expect(res.default).toBeInstanceOf(Error) | ||
}) | ||
it('should work if transformOption is provided', async () => { | ||
@@ -67,7 +95,2 @@ const res = await importFromString("export default function(): string { return 'hi' }", { | ||
}) | ||
it('should be able to access import.meta.url', async () => { | ||
const res = await importFromString('export const { url } = import.meta') | ||
expect(res.url).toMatch(`file://${__dirname}`) | ||
}) | ||
}) | ||
@@ -74,0 +97,0 @@ |
@@ -18,3 +18,3 @@ import path from 'path' | ||
it('should work with relative path import', () => { | ||
it('should work with relative path require', () => { | ||
const modulePath = '../cjs/fixtures/defaultExport.js' | ||
@@ -33,3 +33,3 @@ const res = requireFromString(`module.exports = require('${modulePath}')`) | ||
it('should work with absolute path import', () => { | ||
it('should work with absolute path require', () => { | ||
const modulePath = path.join(__dirname, '../cjs/fixtures/defaultExport.js') | ||
@@ -61,1 +61,15 @@ const res = requireFromString(`module.exports = require('${modulePath}')`) | ||
}) | ||
it('should have access the global object', () => { | ||
const res = requireFromString('module.exports = global.hi', { | ||
globals: { hi: 'hi' } | ||
}) | ||
expect(res).toBe('hi') | ||
}) | ||
it('should work with current global', () => { | ||
const res = requireFromString('module.exports = new Error()', { | ||
useCurrentGlobal: true | ||
}) | ||
expect(res).toBeInstanceOf(Error) | ||
}) |
module.exports = { | ||
extends: ['./node_modules/ts-standardx/.eslintrc.js'], | ||
ignorePatterns: ['dist/*'] | ||
ignorePatterns: ['dist/**/*'], | ||
overrides: [ | ||
{ | ||
files: ['src/**/*.ts'], | ||
rules: { | ||
'@typescript-eslint/no-explicit-any': 'off' | ||
} | ||
} | ||
] | ||
} |
@@ -0,1 +1,3 @@ | ||
/// <reference types="node" /> | ||
import { Context } from 'vm'; | ||
import { TransformOptions } from 'esbuild'; | ||
@@ -5,5 +7,7 @@ | ||
dirname?: string; | ||
globals?: Record<string, unknown>; | ||
globals?: Context; | ||
useCurrentGlobal?: boolean; | ||
} | ||
declare const requireFromString: (code: string, { dirname, globals }?: Options) => any; | ||
declare const requireFromString: (code: string, { dirname, globals, useCurrentGlobal }?: Options) => any; | ||
declare const createRequireFromString: (options?: Options | undefined) => (code: string) => any; | ||
@@ -13,5 +17,7 @@ interface ImportOptions extends Options { | ||
} | ||
declare const importFromString: (code: string, { dirname, globals, transformOptions }?: ImportOptions) => Promise<any>; | ||
declare const importFromStringSync: (code: string, { dirname, globals, transformOptions }?: ImportOptions) => any; | ||
declare const importFromString: (code: string, { transformOptions, ...commonOptions }?: ImportOptions) => Promise<any>; | ||
declare const createImportFromString: (options?: ImportOptions | undefined) => (code: string) => Promise<any>; | ||
declare const importFromStringSync: (code: string, { transformOptions, ...commonOptions }?: ImportOptions) => any; | ||
declare const createImportFromStringSync: (options?: ImportOptions | undefined) => (code: string) => any; | ||
export { ImportOptions, Options, importFromString, importFromStringSync, requireFromString }; | ||
export { ImportOptions, Options, createImportFromString, createImportFromStringSync, createRequireFromString, importFromString, importFromStringSync, requireFromString }; |
@@ -21,15 +21,24 @@ "use strict"; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
var __objRest = (source, exclude) => { | ||
var target = {}; | ||
for (var prop in source) | ||
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) | ||
target[prop] = source[prop]; | ||
if (source != null && __getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(source)) { | ||
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) | ||
target[prop] = source[prop]; | ||
} | ||
return target; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var module$1 = require("module"); | ||
var path = require("path"); | ||
var vm = require("vm"); | ||
var nanoid = require("nanoid"); | ||
var url = require("url"); | ||
var esbuild = require("esbuild"); | ||
function _interopDefaultLegacy(e) { | ||
return e && typeof e === "object" && "default" in e ? e : { "default": e }; | ||
} | ||
var path__default = /* @__PURE__ */ _interopDefaultLegacy(path); | ||
var vm__default = /* @__PURE__ */ _interopDefaultLegacy(vm); | ||
var url__default = /* @__PURE__ */ _interopDefaultLegacy(url); | ||
const module$1 = require("module"); | ||
const path = require("path"); | ||
const vm = require("vm"); | ||
const nanoid = require("nanoid"); | ||
const url = require("url"); | ||
const esbuild = require("esbuild"); | ||
const async = require("nanoid/async"); | ||
const _interopDefaultLegacy = (e) => e && typeof e === "object" && "default" in e ? e : { default: e }; | ||
const vm__default = /* @__PURE__ */ _interopDefaultLegacy(vm); | ||
const isInESModuleScope = () => { | ||
@@ -42,11 +51,12 @@ try { | ||
}; | ||
const isVMModuleAvailable = () => vm__default["default"].Module !== void 0; | ||
const isVMModuleAvailable = () => vm__default.default.Module !== void 0; | ||
const FILE_URL_SCHEME = "file:"; | ||
const fileURLToPath = (value) => value.startsWith(FILE_URL_SCHEME) ? url__default["default"].fileURLToPath(value) : value; | ||
const pathToFileURL = (value) => (value.startsWith(FILE_URL_SCHEME) ? new URL(value) : url__default["default"].pathToFileURL(value)).href; | ||
const FUNCTION_NAMES = [ | ||
const isFileURL = (value) => value.startsWith(FILE_URL_SCHEME); | ||
const fileURLStringToPath = (value) => isFileURL(value) ? url.fileURLToPath(value) : value; | ||
const pathToFileURLString = (value) => (isFileURL(value) ? new url.URL(value) : url.pathToFileURL(value)).toString(); | ||
const internalFunctionNames = [ | ||
"getCallerDirname", | ||
"requireFromString", | ||
"importFromStringSync", | ||
"importFromString", | ||
"importFromStringSync", | ||
"processTicksAndRejections" | ||
@@ -59,15 +69,24 @@ ]; | ||
const functionName = callSite.getFunctionName(); | ||
return functionName === null || !FUNCTION_NAMES.includes(functionName); | ||
return functionName === null || !internalFunctionNames.includes(functionName); | ||
}); | ||
Error.prepareStackTrace = __prepareStackTrace; | ||
const callerFilename = callSites[0].getFileName(); | ||
return path__default["default"].dirname(callerFilename === null ? process.argv[1] : fileURLToPath(callerFilename)); | ||
return path.dirname(callerFilename === null ? process.argv[1] : fileURLStringToPath(callerFilename)); | ||
}; | ||
const resolveModuleSpecifier = (specifier, dirname) => { | ||
const specifierPath = fileURLToPath(specifier); | ||
return specifierPath.startsWith(".") || path__default["default"].isAbsolute(specifierPath) ? path__default["default"].resolve(dirname, specifierPath) : specifier; | ||
const createGlobalProxy = (contextObject) => new Proxy(contextObject, { | ||
get: (target, propKey) => { | ||
if (propKey in target) { | ||
return target[propKey]; | ||
} else { | ||
return Reflect.get(global, propKey); | ||
} | ||
} | ||
}); | ||
const resolveModuleSpecifier = (specifier, dirname2) => { | ||
const specifierPath = fileURLStringToPath(specifier); | ||
return specifierPath.startsWith(".") || path.isAbsolute(specifierPath) ? path.resolve(dirname2, specifierPath) : specifier; | ||
}; | ||
const requireFromString = (code, { dirname = getCallerDirname(), globals } = {}) => { | ||
const requireFromString = (code, { dirname = getCallerDirname(), globals, useCurrentGlobal = false } = {}) => { | ||
var _a; | ||
const moduleFilename = path__default["default"].join(dirname, `${nanoid.nanoid()}.js`); | ||
const moduleFilename = path.join(dirname, `${nanoid.nanoid()}.js`); | ||
const mainModule = isInESModuleScope() ? void 0 : require.main; | ||
@@ -79,3 +98,3 @@ const contextModule = new module$1.Module(moduleFilename, mainModule); | ||
contextModule.require = module$1.createRequire(moduleFilename); | ||
vm__default["default"].runInNewContext(code, __spreadValues({ | ||
const contextObject = __spreadValues({ | ||
__dirname: contextModule.path, | ||
@@ -86,3 +105,7 @@ __filename: contextModule.filename, | ||
require: contextModule.require | ||
}, globals), { | ||
}, globals); | ||
const context = vm.createContext(useCurrentGlobal ? createGlobalProxy(contextObject) : contextObject); | ||
context.global = context; | ||
vm.runInContext(code, context, { | ||
filename: moduleFilename, | ||
async importModuleDynamically(specifier) { | ||
@@ -95,4 +118,6 @@ return await import(resolveModuleSpecifier(specifier, contextModule.path)); | ||
}; | ||
const IMPORT_META_URL_SHIM = "var import_meta_url = require('url').pathToFileURL(__filename).toString();"; | ||
const IMPORT_META_RESOLVE_SHIM = `var import_meta_resolve = () => { | ||
const createRequireFromString = (options) => (code) => requireFromString(code, options); | ||
const USE_STRICT = '"use strict";'; | ||
const IMPORT_META_URL_SHIM = 'var import_meta_url = require("url").pathToFileURL(__filename).toString();'; | ||
const IMPORT_META_RESOLVE_SHIM = `function import_meta_resolve() { | ||
throw new Error( | ||
@@ -103,8 +128,10 @@ \`'import.meta.resolve' is not supported | ||
); | ||
};`; | ||
}`; | ||
const getCommonJS = (transformOptions) => { | ||
var _a; | ||
return __spreadProps(__spreadValues({}, transformOptions), { | ||
banner: `${(transformOptions == null ? void 0 : transformOptions.banner) === void 0 ? "" : `${transformOptions.banner} | ||
`}${IMPORT_META_URL_SHIM} | ||
${IMPORT_META_RESOLVE_SHIM}`, | ||
banner: `${USE_STRICT} | ||
${IMPORT_META_URL_SHIM} | ||
${IMPORT_META_RESOLVE_SHIM} | ||
${(_a = transformOptions == null ? void 0 : transformOptions.banner) != null ? _a : ""}`, | ||
define: __spreadValues({ | ||
@@ -118,9 +145,10 @@ "import.meta.url": "import_meta_url", | ||
const ERR_REQUIRE_ESM = "ERR_REQUIRE_ESM"; | ||
const importFromString = async (code, { dirname = getCallerDirname(), globals, transformOptions } = {}) => { | ||
const importFromString = async (code, _a = {}) => { | ||
var _b = _a, { transformOptions } = _b, commonOptions = __objRest(_b, ["transformOptions"]); | ||
if (!isVMModuleAvailable()) { | ||
const { code: transformedCode2 } = await esbuild.transform(code, getCommonJS(transformOptions)); | ||
try { | ||
return requireFromString(transformedCode2, { dirname, globals }); | ||
return requireFromString(transformedCode2, commonOptions); | ||
} catch (err) { | ||
if (err.code === ERR_REQUIRE_ESM) { | ||
if (err != null && err.code === ERR_REQUIRE_ESM) { | ||
throw new Error(`'import' statement of ES modules is not supported | ||
@@ -132,15 +160,23 @@ Enable '--experimental-vm-modules' CLI option or replace it with dynamic 'import()' expression.`); | ||
} | ||
const { code: transformedCode = void 0 } = transformOptions === void 0 ? {} : await esbuild.transform(code, __spreadValues({ | ||
format: "esm" | ||
}, transformOptions)); | ||
const moduleFilename = path__default["default"].join(dirname, `${nanoid.nanoid()}.js`); | ||
const moduleFileURL = pathToFileURL(moduleFilename); | ||
const context = vm__default["default"].createContext(__spreadValues({ | ||
__IMPORTS__: {} | ||
}, globals)); | ||
const vmModule = new vm__default["default"].SourceTextModule(transformedCode != null ? transformedCode : code, { | ||
identifier: moduleFileURL, | ||
let transformedCode; | ||
if (transformOptions !== void 0) { | ||
({ code: transformedCode } = await esbuild.transform(code, __spreadValues({ | ||
format: "esm" | ||
}, transformOptions))); | ||
} | ||
const { dirname = getCallerDirname(), globals, useCurrentGlobal = false } = commonOptions; | ||
const moduleFilename = path.join(dirname, `${await async.nanoid()}.js`); | ||
const moduleFileURLString = pathToFileURLString(moduleFilename); | ||
const contextObject = __spreadValues({ | ||
__IMPORTS__: {}, | ||
__dirname: dirname, | ||
__filename: moduleFilename | ||
}, globals); | ||
const context = vm.createContext(useCurrentGlobal ? createGlobalProxy(contextObject) : contextObject); | ||
context.global = context; | ||
const vmModule = new vm__default.default.SourceTextModule(transformedCode != null ? transformedCode : code, { | ||
identifier: moduleFileURLString, | ||
context, | ||
initializeImportMeta(meta) { | ||
meta.url = moduleFileURL; | ||
meta.url = moduleFileURLString; | ||
}, | ||
@@ -155,6 +191,6 @@ async importModuleDynamically(specifier) { | ||
context.__IMPORTS__[specifier] = targetModule; | ||
const exportedNames = Object.getOwnPropertyNames(targetModule); | ||
const exportedNames = Object.keys(targetModule); | ||
const targetModuleContent = `${exportedNames.includes("default") ? `export default __IMPORTS__['${specifier}'].default; | ||
` : ""}export const { ${exportedNames.filter((exportedName) => exportedName !== "default").join(", ")} } = __IMPORTS__['${specifier}'];`; | ||
return new vm__default["default"].SourceTextModule(targetModuleContent, { | ||
return new vm__default.default.SourceTextModule(targetModuleContent, { | ||
identifier: resolvedSpecifier, | ||
@@ -168,8 +204,10 @@ context | ||
}; | ||
const importFromStringSync = (code, { dirname = getCallerDirname(), globals, transformOptions } = {}) => { | ||
const createImportFromString = (options) => async (code) => await importFromString(code, options); | ||
const importFromStringSync = (code, _c = {}) => { | ||
var _d = _c, { transformOptions } = _d, commonOptions = __objRest(_d, ["transformOptions"]); | ||
const { code: transformedCode } = esbuild.transformSync(code, getCommonJS(transformOptions)); | ||
try { | ||
return requireFromString(transformedCode, { dirname, globals }); | ||
return requireFromString(transformedCode, commonOptions); | ||
} catch (err) { | ||
if (err.code === ERR_REQUIRE_ESM) { | ||
if (err != null && err.code === ERR_REQUIRE_ESM) { | ||
throw new Error(`'import' statement of ES modules is not supported | ||
@@ -181,4 +219,8 @@ Use asynchronous function 'importFromString' instead or replace it with dynamic 'import()' expression.`); | ||
}; | ||
const createImportFromStringSync = (options) => (code) => importFromStringSync(code, options); | ||
exports.createImportFromString = createImportFromString; | ||
exports.createImportFromStringSync = createImportFromStringSync; | ||
exports.createRequireFromString = createRequireFromString; | ||
exports.importFromString = importFromString; | ||
exports.importFromStringSync = importFromStringSync; | ||
exports.requireFromString = requireFromString; |
{ | ||
"name": "module-from-string", | ||
"version": "3.1.4", | ||
"version": "3.2.0-0", | ||
"description": "Load module from string using require or import.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
39782
760
1