Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: pulls semver-try-require functionality in & removes the dependency #939

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 56 additions & 2 deletions .dependency-cruiser-known-violations.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,49 @@
[
{
"type": "dependency",
"from": "test/utl/try-import.spec.mjs",
"to": "test/utl/node_modules/beta/index.js",
"rule": {
"severity": "error",
"name": "no-non-package-json"
}
},
{
"type": "dependency",
"from": "test/utl/try-import.spec.mjs",
"to": "test/utl/node_modules/no-default-export/main.mjs",
"rule": {
"severity": "error",
"name": "no-non-package-json"
}
},
{
"type": "dependency",
"from": "test/utl/try-import.spec.mjs",
"to": "test/utl/node_modules/rc/index.js",
"rule": {
"severity": "error",
"name": "no-non-package-json"
}
},
{
"type": "dependency",
"from": "test/utl/try-require.spec.cjs",
"to": "test/utl/node_modules/beta/index.js",
"rule": {
"severity": "error",
"name": "no-non-package-json"
}
},
{
"type": "dependency",
"from": "test/utl/try-require.spec.cjs",
"to": "test/utl/node_modules/rc/index.js",
"rule": {
"severity": "error",
"name": "no-non-package-json"
}
},
{
"type": "module",
"from": "src/graph-utl/consolidate-to-folder.mjs",
Expand Down Expand Up @@ -46,8 +91,8 @@
},
{
"type": "module",
"from": "src/utl/array-util.mjs",
"to": "src/utl/array-util.mjs",
"from": "src/utl/extract-root-module-name.cjs",
"to": "src/utl/extract-root-module-name.cjs",
"rule": {
"severity": "info",
"name": "utl-module-not-shared-enough"
Expand All @@ -62,6 +107,15 @@
"name": "utl-module-not-shared-enough"
}
},
{
"type": "module",
"from": "src/utl/try-require.cjs",
"to": "src/utl/try-require.cjs",
"rule": {
"severity": "info",
"name": "utl-module-not-shared-enough"
}
},
{
"type": "module",
"from": "src/utl/wrap-and-indent.mjs",
Expand Down
9 changes: 8 additions & 1 deletion .dependency-cruiser.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@
{
"name": "not-to-unresolvable",
"comment": "This module tried to depend on something that can't be resolved to disk. Revise yer code",
"from": {},
"from": {
"pathNot": [
// those import 'local node_modules' which, on e.g. yarn berry are not
// resolvable. Still ok, though, as for the (test) purpose & under e.g.
// npm they still work 100% ok
"^test/utl/(?:try-require[.]spec[.]cjs|try-import[.]spec[.]mjs)$"
]
},
"to": {
"couldNotResolve": true,
"exoticallyRequired": false
Expand Down
12 changes: 0 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"electrovir (https://github.com/electrovir)",
"fusheng (https://github.com/lin-hun)"
],
"type": "commonjs",
"license": "MIT",
"repository": {
"type": "git",
Expand Down Expand Up @@ -224,7 +225,6 @@
"rechoir": "^0.8.0",
"safe-regex": "2.1.1",
"semver": "^7.6.2",
"semver-try-require": "7.0.0",
"teamcity-service-messages": "0.1.14",
"tsconfig-paths-webpack-plugin": "4.1.0",
"watskeburt": "4.0.2",
Expand Down
2 changes: 1 addition & 1 deletion src/config-utl/extract-babel-config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { readFile } from "node:fs/promises";

import { extname } from "node:path";
import json5 from "json5";
import tryImport from "semver-try-require";
import makeAbsolute from "./make-absolute.mjs";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

async function getJSConfig(pBabelConfigFileName) {
Expand Down
2 changes: 1 addition & 1 deletion src/config-utl/extract-ts-config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { dirname, resolve } from "node:path";
import tryImport from "semver-try-require";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

const typescript = await tryImport(
Expand Down
2 changes: 1 addition & 1 deletion src/extract/swc/dependency-visitor.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-inline-comments */
/* eslint-disable max-classes-per-file */
import tryImport from "semver-try-require";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

/** @type {import('@swc/core/Visitor')} */
Expand Down
2 changes: 1 addition & 1 deletion src/extract/swc/parse.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import tryImport from "semver-try-require";
import memoize, { memoizeClear } from "memoize";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

/** @type {import('@swc/core')} */
Expand Down
2 changes: 1 addition & 1 deletion src/extract/transpile/babel-wrap.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import tryImport from "semver-try-require";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

const babel = await tryImport("@babel/core", meta.supportedTranspilers.babel);
Expand Down
2 changes: 1 addition & 1 deletion src/extract/transpile/coffeescript-wrap.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import tryImport from "semver-try-require";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

/*
Expand Down
2 changes: 1 addition & 1 deletion src/extract/transpile/livescript-wrap.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import tryImport from "semver-try-require";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

const livescript = await tryImport(
Expand Down
2 changes: 1 addition & 1 deletion src/extract/transpile/svelte-wrap.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import tryImport from "semver-try-require";
import preProcess from "./svelte-preprocess.mjs";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

const { compile } = await tryImport(
Expand Down
2 changes: 1 addition & 1 deletion src/extract/transpile/typescript-wrap.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import tryImport from "semver-try-require";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

const typescript = await tryImport(
Expand Down
2 changes: 1 addition & 1 deletion src/extract/transpile/vue-template-wrap.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { EOL } = require("node:os");
const isEmpty = require("lodash/isEmpty");
const tryRequire = require("semver-try-require");
const tryRequire = require("#utl/try-require.cjs");
const meta = require("#meta.cjs");

/*
Expand Down
2 changes: 1 addition & 1 deletion src/extract/tsc/extract-typescript-deps.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable max-lines */
/* eslint-disable no-inline-comments */
import tryImport from "semver-try-require";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";

/** @type {import("typescript")} */
Expand Down
2 changes: 1 addition & 1 deletion src/extract/tsc/parse.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs";
import tryImport from "semver-try-require";
import memoize, { memoizeClear } from "memoize";
import transpile from "../transpile/index.mjs";
import tryImport from "#utl/try-import.mjs";
import meta from "#meta.cjs";
import getExtension from "#utl/get-extension.mjs";

Expand Down
23 changes: 23 additions & 0 deletions src/utl/extract-root-module-name.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const LOCAL_MODULE_RE = /^[.]{1,2}($|\/.*)/g;
const ABSOLUTE_MODULE_RE = /^\/.*/g;

const PACKAGE_RE = "[^/]+";
const SCOPED_PACKAGE_RE = "@[^/]+(/[^/]+)";
const ROOT_MODULE_RE = new RegExp(`^(${SCOPED_PACKAGE_RE}|${PACKAGE_RE})`, "g");

/**
* returns the module name that likely contains the package.json
*
* @param {string} pModuleName module name string as you'd require it
* @returns {string|undefined} the module name that likely contains the package.json
*/
module.exports = function extractRootModuleName(pModuleName) {
if (
pModuleName.match(LOCAL_MODULE_RE) ||
pModuleName.match(ABSOLUTE_MODULE_RE)
) {
return pModuleName;
} else {
return (pModuleName.match(ROOT_MODULE_RE) || []).shift();
}
};
67 changes: 67 additions & 0 deletions src/utl/try-import.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { join } from "node:path/posix";
import { createRequire } from "node:module";
import { coerce, satisfies } from "semver";
import extractRootModuleName from "./extract-root-module-name.cjs";

const require = createRequire(import.meta.url);

/**
* @throws {Error}
* @param {string} pModuleName
* @returns {string}
*/
function getVersion(pModuleName) {
// // The 'proper' way to do this would be with a dynamic import with an
// // import assertion. Because it's 'experimental' since node 16 and prints
// // an ugly warning on stderr since node 19 we'll be using the require
// // hack below in stead.
// const lManifest = await import(
// // @ts-expect-error TS2345 extractRootModuleName can return either a string or
// // undefined. If undefined this function will throw. Which is _fine_, even
// // _expected_ in the context it's currently used
// path.join(extractRootModuleName(pModuleName), "package.json"),
// { assert: { type: "json" } }
// );
// // changes the return type to Promise<string>
// return lManifest.default.version;
// eslint-disable-next-line import/no-dynamic-require, security/detect-non-literal-require
const lManifest = require(
join(
// @ts-expect-error TS2345 extractRootModuleName can return either a string or
// undefined. If undefined this function will throw. Which is _fine_, even
// _expected_ in the context it's currently used
extractRootModuleName(pModuleName),
"package.json",
),
);
return lManifest.version;
}

/**
* Tries to import a module and optionally checks its version.
*
* @param {string} pModuleName - The name of the module to import.
* @param {string} [pSemanticVersion] - An semantic version to check against.
* @returns {Promise<NodeModule | false>} - The imported module or false if the import fails or the version does not satisfy the provided semantic version.
*/

// eslint-disable-next-line complexity
export default async function tryImport(pModuleName, pSemanticVersion) {
try {
if (pSemanticVersion) {
const lVersion = getVersion(pModuleName);
const lCoerced = coerce(lVersion);
if (
lVersion &&
lCoerced &&
!satisfies(lCoerced.version, pSemanticVersion)
) {
return false;
}
}
const lModule = await import(pModuleName);
return lModule.default ? lModule.default : lModule;
} catch (pError) {
return false;
}
}
51 changes: 51 additions & 0 deletions src/utl/try-require.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const { join } = require("node:path");
const satisfies = require("semver/functions/satisfies");
const coerce = require("semver/functions/coerce");
const extractRootModuleName = require("./extract-root-module-name.cjs");

/**
* @throws {Error}
* @param pModuleName the name of the module to get the version for
* @return the version of the module identified by pModuleName
*/
function getVersion(pModuleName) {
// @ts-expect-error TS2345 extractRootModuleName can return either a string or
// undefined. If undefined this function will throw. Which is _fine_, even
// _expected_ in the context it's currently used
// eslint-disable-next-line import/no-dynamic-require, node/global-require, security/detect-non-literal-require
return require(join(extractRootModuleName(pModuleName), "package.json"))
.version;
}

/**
* returns the (resolved) module identified by pModuleName:
* - if it is available, and
* - it satisfies the semantic version range specified by pSemVer
*
* returns false in all other cases
*
* @param {string} pModuleName the name of the module to resolve
* @param {string} [pSemanticVersion] (optional) a semantic version (range)
* @return {NodeModule | false }the (resolved) module identified by pModuleName or false
*/
function tryRequire(pModuleName, pSemanticVersion) {
try {
if (pSemanticVersion) {
const lVersion = getVersion(pModuleName);
const lCoerced = coerce(lVersion);
if (
lVersion &&
lCoerced &&
!satisfies(lCoerced.version, pSemanticVersion)
) {
return false;
}
}
// eslint-disable-next-line import/no-dynamic-require, node/global-require, security/detect-non-literal-require
return require(pModuleName);
} catch (pError) {
return false;
}
}

module.exports = tryRequire;
2 changes: 1 addition & 1 deletion test/extract/transpile/vue3-template-wrap.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url));
const wrap = proxyquire.load("#extract/transpile/vue-template-wrap.cjs", {
// Force the tryRequire on "vue-template-compiler" to fail
// so that we ensure we are using Vue 3 for this test
"semver-try-require": (pModuleName) =>
"#utl/try-require.cjs": (pModuleName) =>
pModuleName === "vue-template-compiler"
? false
: require("@vue/compiler-sfc"),
Expand Down
Loading