mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
241 lines
No EOL
9.2 KiB
Diff
241 lines
No EOL
9.2 KiB
Diff
diff --git a/lib/module-loader.js b/lib/module-loader.js
|
|
index e28f1254473d53c5f0b21c390f19ced16dc1d27b..4907936a8252e02f59145bd3297512d93302d4d8 100644
|
|
--- a/lib/module-loader.js
|
|
+++ b/lib/module-loader.js
|
|
@@ -127,7 +127,21 @@ class ModuleLoader {
|
|
return config.type !== undefined;
|
|
};
|
|
try {
|
|
- filePath = require.resolve(modulePath);
|
|
+ try {
|
|
+ // We wrap it with try/catch and fallback to custom path if it fails to make it compatible with Hive.
|
|
+ // Due to some weird behavior in tsup/esbuild, it fails to resolve the path to the module.
|
|
+ filePath = require.resolve(modulePath);
|
|
+ } catch (error) {
|
|
+ const customPath = process.env.OCLIF_CLI_CUSTOM_PATH;
|
|
+ if (typeof customPath !== "string") {
|
|
+ throw error;
|
|
+ }
|
|
+ modulePath = modulePath.replace('/src/', '/dist/').replace('\\src\\', '\\dist\\');
|
|
+ filePath = require.resolve(
|
|
+ path.resolve(customPath, modulePath) + ".js"
|
|
+ );
|
|
+ }
|
|
+
|
|
isESM = ModuleLoader.isPathModule(filePath);
|
|
}
|
|
catch {
|
|
diff --git a/lib/module-loader.modified.js b/lib/module-loader.modified.js
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bd3bf6cda2c0537590ee13860bbe36fbfb3c24d3
|
|
--- /dev/null
|
|
+++ b/lib/module-loader.modified.js
|
|
@@ -0,0 +1,208 @@
|
|
+"use strict";
|
|
+Object.defineProperty(exports, "__esModule", { value: true });
|
|
+const path = require("path");
|
|
+const url = require("url");
|
|
+const fs = require("fs-extra");
|
|
+const errors_1 = require("./errors");
|
|
+const Config = require("./config");
|
|
+const getPackageType = require("get-package-type");
|
|
+/**
|
|
+ * Defines file extension resolution when source files do not have an extension.
|
|
+ */
|
|
+// eslint-disable-next-line camelcase
|
|
+const s_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs"];
|
|
+/**
|
|
+ * Provides a mechanism to use dynamic import / import() with tsconfig -> module: commonJS as otherwise import() gets
|
|
+ * transpiled to require().
|
|
+ */
|
|
+const _importDynamic = new Function("modulePath", "return import(modulePath)"); // eslint-disable-line no-new-func
|
|
+/**
|
|
+ * Provides a static class with several utility methods to work with Oclif config / plugin to load ESM or CJS Node
|
|
+ * modules and source files.
|
|
+ *
|
|
+ * @author Michael Leahy <support@typhonjs.io> (https://github.com/typhonrt)
|
|
+ */
|
|
+// eslint-disable-next-line unicorn/no-static-only-class
|
|
+class ModuleLoader {
|
|
+ /**
|
|
+ * Loads and returns a module.
|
|
+ *
|
|
+ * Uses `getPackageType` to determine if `type` is set to 'module. If so loads '.js' files as ESM otherwise uses
|
|
+ * a bare require to load as CJS. Also loads '.mjs' files as ESM.
|
|
+ *
|
|
+ * Uses dynamic import to load ESM source or require for CommonJS.
|
|
+ *
|
|
+ * A unique error, ModuleLoadError, combines both CJS and ESM loader module not found errors into a single error that
|
|
+ * provides a consistent stack trace and info.
|
|
+ *
|
|
+ * @param {IConfig|IPlugin} config - Oclif config or plugin config.
|
|
+ * @param {string} modulePath - NPM module name or file path to load.
|
|
+ *
|
|
+ * @returns {Promise<*>} The entire ESM module from dynamic import or CJS module by require.
|
|
+ */
|
|
+ static async load(config, modulePath) {
|
|
+ let filePath;
|
|
+ let isESM;
|
|
+ try {
|
|
+ ({ isESM, filePath } = ModuleLoader.resolvePath(config, modulePath));
|
|
+ // It is important to await on _importDynamic to catch the error code.
|
|
+ return isESM
|
|
+ ? await _importDynamic(url.pathToFileURL(filePath))
|
|
+ : require(filePath);
|
|
+ } catch (error) {
|
|
+ if (
|
|
+ error.code === "MODULE_NOT_FOUND" ||
|
|
+ error.code === "ERR_MODULE_NOT_FOUND"
|
|
+ ) {
|
|
+ throw new errors_1.ModuleLoadError(
|
|
+ `${isESM ? "import()" : "require"} failed to load ${
|
|
+ filePath || modulePath
|
|
+ }`
|
|
+ );
|
|
+ }
|
|
+ throw error;
|
|
+ }
|
|
+ }
|
|
+ /**
|
|
+ * Loads a module and returns an object with the module and data about the module.
|
|
+ *
|
|
+ * Uses `getPackageType` to determine if `type` is set to `module`. If so loads '.js' files as ESM otherwise uses
|
|
+ * a bare require to load as CJS. Also loads '.mjs' files as ESM.
|
|
+ *
|
|
+ * Uses dynamic import to load ESM source or require for CommonJS.
|
|
+ *
|
|
+ * A unique error, ModuleLoadError, combines both CJS and ESM loader module not found errors into a single error that
|
|
+ * provides a consistent stack trace and info.
|
|
+ *
|
|
+ * @param {IConfig|IPlugin} config - Oclif config or plugin config.
|
|
+ * @param {string} modulePath - NPM module name or file path to load.
|
|
+ *
|
|
+ * @returns {Promise<{isESM: boolean, module: *, filePath: string}>} An object with the loaded module & data including
|
|
+ * file path and whether the module is ESM.
|
|
+ */
|
|
+ static async loadWithData(config, modulePath) {
|
|
+ let filePath;
|
|
+ let isESM;
|
|
+ try {
|
|
+ ({ isESM, filePath } = ModuleLoader.resolvePath(config, modulePath));
|
|
+ const module = isESM
|
|
+ ? await _importDynamic(url.pathToFileURL(filePath))
|
|
+ : require(filePath);
|
|
+ return { isESM, module, filePath };
|
|
+ } catch (error) {
|
|
+ if (
|
|
+ error.code === "MODULE_NOT_FOUND" ||
|
|
+ error.code === "ERR_MODULE_NOT_FOUND"
|
|
+ ) {
|
|
+ throw new errors_1.ModuleLoadError(
|
|
+ `${isESM ? "import()" : "require"} failed to load ${
|
|
+ filePath || modulePath
|
|
+ }: ${error.message}`
|
|
+ );
|
|
+ }
|
|
+ throw error;
|
|
+ }
|
|
+ }
|
|
+ /**
|
|
+ * For `.js` files uses `getPackageType` to determine if `type` is set to `module` in associated `package.json`. If
|
|
+ * the `modulePath` provided ends in `.mjs` it is assumed to be ESM.
|
|
+ *
|
|
+ * @param {string} filePath - File path to test.
|
|
+ *
|
|
+ * @returns {boolean} The modulePath is an ES Module.
|
|
+ * @see https://www.npmjs.com/package/get-package-type
|
|
+ */
|
|
+ static isPathModule(filePath) {
|
|
+ const extension = path.extname(filePath).toLowerCase();
|
|
+ switch (extension) {
|
|
+ case ".js":
|
|
+ return getPackageType.sync(filePath) === "module";
|
|
+ case ".ts":
|
|
+ return getPackageType.sync(filePath) === "module";
|
|
+ case ".mjs":
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ /**
|
|
+ * Resolves a modulePath first by `require.resolve` to allow Node to resolve an actual module. If this fails then
|
|
+ * the `modulePath` is resolved from the root of the provided config. `Config.tsPath` is used for initial resolution.
|
|
+ * If this file path does not exist then several extensions are tried from `s_EXTENSIONS` in order: '.js', '.mjs',
|
|
+ * '.cjs'. After a file path has been selected `isPathModule` is used to determine if the file is an ES Module.
|
|
+ *
|
|
+ * @param {IConfig|IPlugin} config - Oclif config or plugin config.
|
|
+ * @param {string} modulePath - File path to load.
|
|
+ *
|
|
+ * @returns {{isESM: boolean, filePath: string}} An object including file path and whether the module is ESM.
|
|
+ */
|
|
+ static resolvePath(config, modulePath) {
|
|
+ let isESM;
|
|
+ let filePath;
|
|
+ const isPlugin = (config) => {
|
|
+ return config.type !== undefined;
|
|
+ };
|
|
+ try {
|
|
+ try {
|
|
+ // We wrap it with try/catch and fallback to custom path if it fails to make it compatible with Hive.
|
|
+ // Due to some weird behavior in tsup/esbuild, it fails to resolve the path to the module.
|
|
+ filePath = require.resolve(modulePath);
|
|
+ } catch (error) {
|
|
+ const customPath = process.env.OCLIF_CLI_CUSTOM_PATH;
|
|
+ if (typeof customPath !== "string") {
|
|
+ throw error;
|
|
+ }
|
|
+ filePath = require.resolve(
|
|
+ path.resolve(customPath, modulePath) + ".js"
|
|
+ );
|
|
+ }
|
|
+ isESM = ModuleLoader.isPathModule(filePath);
|
|
+ } catch {
|
|
+ filePath = isPlugin(config)
|
|
+ ? Config.tsPath(config.root, modulePath, config.type)
|
|
+ : Config.tsPath(config.root, modulePath);
|
|
+ let fileExists = false;
|
|
+ let isDirectory = false;
|
|
+ if (fs.existsSync(filePath)) {
|
|
+ fileExists = true;
|
|
+ try {
|
|
+ if (fs.lstatSync(filePath)?.isDirectory?.()) {
|
|
+ fileExists = false;
|
|
+ isDirectory = true;
|
|
+ }
|
|
+ } catch {}
|
|
+ }
|
|
+ if (!fileExists) {
|
|
+ // Try all supported extensions.
|
|
+ let foundPath = ModuleLoader.findFile(filePath);
|
|
+ if (!foundPath && isDirectory) {
|
|
+ // Since filePath is a directory, try looking for index file.
|
|
+ foundPath = ModuleLoader.findFile(path.join(filePath, "index"));
|
|
+ }
|
|
+ if (foundPath) {
|
|
+ filePath = foundPath;
|
|
+ }
|
|
+ }
|
|
+ isESM = ModuleLoader.isPathModule(filePath);
|
|
+ }
|
|
+ return { isESM, filePath };
|
|
+ }
|
|
+ /**
|
|
+ * Try adding the different extensions from `s_EXTENSIONS` to find the file.
|
|
+ *
|
|
+ * @param {string} filePath - File path to load.
|
|
+ *
|
|
+ * @returns {string | null} Modified file path including extension or null if file is not found.
|
|
+ */
|
|
+ static findFile(filePath) {
|
|
+ // eslint-disable-next-line camelcase
|
|
+ for (const extension of s_EXTENSIONS) {
|
|
+ const testPath = `${filePath}${extension}`;
|
|
+ if (fs.existsSync(testPath)) {
|
|
+ return testPath;
|
|
+ }
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+exports.default = ModuleLoader;
|