diff --git a/doc/cli.md b/doc/cli.md index e8dc4a204..18192c9f9 100644 --- a/doc/cli.md +++ b/doc/cli.md @@ -22,7 +22,7 @@ available in dependency-cruiser configurations. 1. [`--init`](#--init) 1. [`--metrics`: calculate stability metrics](#--metrics) 1. [`--no-metrics`: do not calculate stability metrics](#--no-metrics) -1. [`--info`: show what alt-js are supported](#--info-showing-what-alt-js-are-supported) +1. [`--info`: show what alt-js are supported](#--info-show-what-alt-js-are-supported) 1. [`--ignore-known`: ignore known violations](#--ignore-known-ignore-known-violations) 1. [`--no-ignore-known`: don't ignore known violations](#--no-ignore-known) 1. [`--help`/ no parameters: get help](#--help--no-parameters) @@ -749,7 +749,7 @@ Do not calculate metrics. You can use this to override an earlier set `--metrics command line option or `metrics` option in a .dependency-cruiser.js configuration file. -### `--info` showing what alt-js are supported +### `--info` show what alt-js are supported Which alt-js languages dependency-cruiser supports depends on the availability it has to them. To see how dependency-cruiser perceives its environment use @@ -759,35 +759,50 @@ it has to them. To see how dependency-cruiser perceives its environment use Typical output ``` -Supported: - - If you need a supported, but not enabled transpiler ('✖' below), install - it in the same folder dependency-cruiser is installed. E.g. 'npm i livescript' - will enable livescript support if it's installed in your project folder. - -Transpilers: - - ✔ javascript (>es1) - ✔ coffee-script (>=1.0.0 <2.0.0) - ✔ coffeescript (>=1.0.0 <3.0.0) - ✖ livescript (>=1.0.0 <2.0.0) - ✔ typescript (>=2.0.0 <4.0.0) - -Extensions: - - ✔ .js - ✔ .mjs - ✔ .jsx - ✔ .vue - ✔ .ts - ✔ .tsx - ✔ .d.ts - ✖ .ls - ✔ .coffee - ✔ .litcoffee - ✔ .coffee.md - ✔ .csx - ✔ .cjsx + dependency-cruiser@16.4.2 + + node version supported : ^18.17||>=20 + node version found : v22.8.0 + os version found : x64 darwin@21.6.0 + + If you need a supported, but not enabled transpiler ('x' below), just install + it in the same folder dependency-cruiser is installed. E.g. 'npm i livescript' + will enable livescript support if it's installed in your project folder. + + ✔ transpiler versions supported version found + - ---------------------- ------------------- ------------------------ + ✔ javascript * acorn@8.12.1 + ✔ babel >=7.0.0 <8.0.0 @babel/core@7.25.2 + ✔ coffee-script >=1.0.0 <2.0.0 coffeescript@2.7.0 + ✔ coffeescript >=1.0.0 <3.0.0 coffeescript@2.7.0 + x livescript >=1.0.0 <2.0.0 - + ✔ svelte >=3.0.0 <5.0.0 svelte/compiler@4.2.19 + ✔ swc >=1.0.0 <2.0.0 @swc/core@1.7.26 + ✔ typescript >=2.0.0 <6.0.0 typescript@5.6.2 + ✔ vue-template-compiler >=2.0.0 <3.0.0 vue-template-compiler + ✔ @vue/compiler-sfc >=3.0.0 <4.0.0 vue-template-compiler + + ✔ extension + - --------- + ✔ .js + ✔ .cjs + ✔ .mjs + ✔ .jsx + ✔ .ts + ✔ .tsx + ✔ .d.ts + ✔ .cts + ✔ .d.cts + ✔ .mts + ✔ .d.mts + ✔ .vue + ✔ .svelte + x .ls + ✔ .coffee + ✔ .litcoffee + ✔ .coffee.md + ✔ .csx + ✔ .cjsx ``` diff --git a/src/cli/format-meta-info.mjs b/src/cli/format-meta-info.mjs index dc39e280f..c0245c92a 100644 --- a/src/cli/format-meta-info.mjs +++ b/src/cli/format-meta-info.mjs @@ -1,19 +1,29 @@ +import { release, platform, arch } from "node:os"; import pc from "picocolors"; import { getAvailableTranspilers, allExtensions } from "#main/index.mjs"; +import meta from "#meta.cjs"; function bool2Symbol(pBool) { return pBool ? pc.green("✔") : pc.red("x"); } +const MAX_VERSION_RANGE_STRING_LENGTH = 19; +const MAX_TRANSPILER_NAME_LENGTH = 22; +const MAX_VERSION_STRING_LENGTH = 24; + function formatTranspilers() { - return getAvailableTranspilers().reduce( - (pAll, pThis) => - `${pAll} ${bool2Symbol(pThis.available)} ${pThis.name} (${ - pThis.version - })\n`, - ` ${bool2Symbol(true)} javascript (>es1)\n`, + let lTranspilerTableHeader = pc.bold( + ` ✔ ${"transpiler".padEnd(MAX_TRANSPILER_NAME_LENGTH)} ${"versions supported".padEnd(MAX_VERSION_RANGE_STRING_LENGTH)} version found`, ); + let lTranspilerTableDivider = ` - ${"-".repeat(MAX_TRANSPILER_NAME_LENGTH)} ${"-".repeat(MAX_VERSION_RANGE_STRING_LENGTH)} ${"-".repeat(MAX_VERSION_STRING_LENGTH)}`; + let lTranspilerTable = getAvailableTranspilers() + .map( + (pTranspiler) => + ` ${bool2Symbol(pTranspiler.available)} ${pTranspiler.name.padEnd(MAX_TRANSPILER_NAME_LENGTH)} ${pTranspiler.version.padEnd(MAX_VERSION_RANGE_STRING_LENGTH)} ${pTranspiler.currentVersion}`, + ) + .join("\n"); + return `${lTranspilerTableHeader}\n${lTranspilerTableDivider}\n${lTranspilerTable}\n`; } function formatExtensions(pExtensions) { @@ -26,7 +36,11 @@ function formatExtensions(pExtensions) { export default function formatMetaInfo() { return ` - Supported: + ${pc.bold("dependency-cruiser")}@${meta.version} + + node version supported : ${meta.engines.node} + node version found : ${process.version} + os version found : ${arch()} ${platform()}@${release()} If you need a supported, but not enabled transpiler ('${pc.red( "x", @@ -34,11 +48,9 @@ export default function formatMetaInfo() { it in the same folder dependency-cruiser is installed. E.g. 'npm i livescript' will enable livescript support if it's installed in your project folder. - Transpilers: - ${formatTranspilers()} - Extensions: - + ${pc.bold("✔ extension")} + - --------- ${formatExtensions(allExtensions)} `; } diff --git a/src/extract/swc/parse.mjs b/src/extract/swc/parse.mjs index 51cfae699..1d85c023b 100644 --- a/src/extract/swc/parse.mjs +++ b/src/extract/swc/parse.mjs @@ -46,3 +46,5 @@ export function clearCache() { */ // @ts-expect-error dfdfd export const isAvailable = () => swc !== false; + +export const version = () => `@swc/core@${swc.version}`; diff --git a/src/extract/transpile/babel-wrap.mjs b/src/extract/transpile/babel-wrap.mjs index 207568eea..cace0227f 100644 --- a/src/extract/transpile/babel-wrap.mjs +++ b/src/extract/transpile/babel-wrap.mjs @@ -5,6 +5,9 @@ const babel = await tryImport("@babel/core", meta.supportedTranspilers.babel); export default { isAvailable: () => babel !== false, + + version: () => `@babel/core@${babel.version}`, + transpile: (pSource, pFileName, pTranspileOptions = {}) => babel.transformSync(pSource, { ...(pTranspileOptions.babelConfig || {}), diff --git a/src/extract/transpile/coffeescript-wrap.mjs b/src/extract/transpile/coffeescript-wrap.mjs index 5890674e1..43a5cf1c4 100644 --- a/src/extract/transpile/coffeescript-wrap.mjs +++ b/src/extract/transpile/coffeescript-wrap.mjs @@ -28,6 +28,9 @@ const coffeeScript = await getCoffeeScriptModule(); export default function coffeeScriptWrap(pLiterate) { return { isAvailable: () => coffeeScript !== false, + + version: () => `coffeescript@${coffeeScript.VERSION}`, + transpile: (pSource) => { const lOptions = pLiterate ? { literate: true } : {}; diff --git a/src/extract/transpile/javascript-wrap.mjs b/src/extract/transpile/javascript-wrap.mjs index 2aa860c2e..33244b6a9 100644 --- a/src/extract/transpile/javascript-wrap.mjs +++ b/src/extract/transpile/javascript-wrap.mjs @@ -1,4 +1,12 @@ +import { createRequire } from "node:module"; + export default { isAvailable: () => true, + + version: () => { + const require = createRequire(import.meta.url); + return `acorn@${require("acorn").version}`; + }, + transpile: (pSource) => pSource, }; diff --git a/src/extract/transpile/livescript-wrap.mjs b/src/extract/transpile/livescript-wrap.mjs index af5e74ad5..c2cc857fe 100644 --- a/src/extract/transpile/livescript-wrap.mjs +++ b/src/extract/transpile/livescript-wrap.mjs @@ -10,6 +10,8 @@ const livescript = await tryImport( export default { isAvailable: () => livescript !== false, + version: () => `livescript@${livescript.VERSION}`, + transpile: (pSource) => livescript.compile(pSource), }; /* c8 ignore stop */ diff --git a/src/extract/transpile/meta.mjs b/src/extract/transpile/meta.mjs index ae5cd9afd..482786289 100644 --- a/src/extract/transpile/meta.mjs +++ b/src/extract/transpile/meta.mjs @@ -1,7 +1,26 @@ /* eslint security/detect-object-injection : 0*/ +import { + isAvailable as swcIsAvailable, + version as swcVersion, +} from "../swc/parse.mjs"; import tryAvailable from "./try-import-available.mjs"; +import javascriptWrap from "./javascript-wrap.mjs"; +import babelWrap from "./babel-wrap.mjs"; +import coffeeScriptWrapFunction from "./coffeescript-wrap.mjs"; +import livescriptWrap from "./livescript-wrap.mjs"; +import svelteWrapFunction from "./svelte-wrap.mjs"; +import typescriptWrapFunction from "./typescript-wrap.mjs"; +import vueTemplateWrap from "./vue-template-wrap.cjs"; import meta from "#meta.cjs"; +const swcWrap = { + isAvailable: () => swcIsAvailable(), + version: () => swcVersion(), +}; +const coffeeScriptWrap = coffeeScriptWrapFunction(false); +const svelteWrap = svelteWrapFunction(javascriptWrap); +const typescriptWrap = typescriptWrapFunction("esm"); + function gotCoffee() { return ( tryAvailable("coffeescript", meta.supportedTranspilers.coffeescript) || @@ -28,6 +47,19 @@ const TRANSPILER2AVAILABLE = { ), }; +const TRANSPILER2WRAPPER = { + babel: babelWrap, + javascript: javascriptWrap, + "coffee-script": coffeeScriptWrap, + coffeescript: coffeeScriptWrap, + livescript: livescriptWrap, + svelte: svelteWrap, + swc: swcWrap, + typescript: typescriptWrap, + "vue-template-compiler": vueTemplateWrap, + "@vue/compiler-sfc": vueTemplateWrap, +}; + export const EXTENSION2AVAILABLE = new Map([ [".js", TRANSPILER2AVAILABLE.javascript], [".cjs", TRANSPILER2AVAILABLE.javascript], @@ -81,6 +113,11 @@ export const allExtensions = Array.from(EXTENSION2AVAILABLE.keys()).map( }), ); +function getCurrentVersion(pTranspiler) { + const lTranspiler = TRANSPILER2WRAPPER[pTranspiler]; + return lTranspiler.isAvailable() ? lTranspiler.version() : "-"; +} + /** * an array of extensions that are 'scannable' (have a valid transpiler * available for) in the current environment. @@ -99,9 +136,12 @@ export const scannableExtensions = Array.from( * @return {IAvailableTranspiler[]} an array of supported transpilers */ export function getAvailableTranspilers() { - return Object.keys(meta.supportedTranspilers).map((pTranspiler) => ({ - name: pTranspiler, - version: meta.supportedTranspilers[pTranspiler], - available: TRANSPILER2AVAILABLE[pTranspiler], - })); + return ["javascript"] + .concat(Object.keys(meta.supportedTranspilers)) + .map((pTranspiler) => ({ + name: pTranspiler, + version: meta.supportedTranspilers[pTranspiler] || "*", + available: TRANSPILER2AVAILABLE[pTranspiler], + currentVersion: getCurrentVersion(pTranspiler), + })); } diff --git a/src/extract/transpile/svelte-wrap.mjs b/src/extract/transpile/svelte-wrap.mjs index fb48b1301..022046e3c 100644 --- a/src/extract/transpile/svelte-wrap.mjs +++ b/src/extract/transpile/svelte-wrap.mjs @@ -2,7 +2,7 @@ import preProcess from "./svelte-preprocess.mjs"; import tryImport from "#utl/try-import.mjs"; import meta from "#meta.cjs"; -const { compile } = await tryImport( +const { compile, VERSION } = await tryImport( "svelte/compiler", meta.supportedTranspilers.svelte, ); @@ -21,6 +21,7 @@ function getTranspiler(pTranspilerWrapper) { export default function svelteWrap(pTranspilerWrapper) { return { isAvailable: () => Boolean(compile), + version: () => `svelte/compiler@${VERSION}`, transpile: getTranspiler(pTranspilerWrapper), }; } diff --git a/src/extract/transpile/typescript-wrap.mjs b/src/extract/transpile/typescript-wrap.mjs index 016b03b04..b59ddbeaa 100644 --- a/src/extract/transpile/typescript-wrap.mjs +++ b/src/extract/transpile/typescript-wrap.mjs @@ -36,6 +36,8 @@ export default function typescriptWrap(pFlavor) { return { isAvailable: () => typescript !== false, + version: () => `typescript@${typescript.version}`, + transpile: (pSource, _pFileName, pTranspileOptions = {}) => typescript.transpileModule(pSource, { ...(pTranspileOptions.tsConfig || {}), diff --git a/src/extract/transpile/vue-template-wrap.cjs b/src/extract/transpile/vue-template-wrap.cjs index 1d5f2df76..e43283a55 100644 --- a/src/extract/transpile/vue-template-wrap.cjs +++ b/src/extract/transpile/vue-template-wrap.cjs @@ -71,6 +71,10 @@ function vue2Transpile(pSource) { module.exports = { isAvailable: () => vueTemplateCompiler !== false, + version: () => + vueTemplateCompiler.version + ? `@vue/compiler-sfc@${vueTemplateCompiler.version}` + : "vue-template-compiler", transpile: (pSource) => isVue3 ? vue3Transpile(pSource) : vue2Transpile(pSource), }; diff --git a/test/extract/transpile/meta.spec.mjs b/test/extract/transpile/meta.spec.mjs index 3fc56b3b6..ba5d90738 100644 --- a/test/extract/transpile/meta.spec.mjs +++ b/test/extract/transpile/meta.spec.mjs @@ -1,4 +1,5 @@ import { deepEqual } from "node:assert/strict"; +import { validRange } from "semver"; import { getAvailableTranspilers, scannableExtensions, @@ -29,52 +30,38 @@ describe("[U] extract/transpile/meta", () => { }); it("returns the available transpilers", () => { - deepEqual(getAvailableTranspilers(), [ - { - name: "babel", - version: ">=7.0.0 <8.0.0", - available: true, - }, - { - name: "coffee-script", - version: ">=1.0.0 <2.0.0", - available: true, - }, - { - name: "coffeescript", - version: ">=1.0.0 <3.0.0", - available: true, - }, - { - name: "livescript", - version: ">=1.0.0 <2.0.0", - available: false, - }, - { - name: "svelte", - version: ">=3.0.0 <5.0.0", - available: true, - }, - { - name: "swc", - version: ">=1.0.0 <2.0.0", - available: true, - }, - { - name: "typescript", - version: ">=2.0.0 <6.0.0", - available: true, - }, - { - name: "vue-template-compiler", - version: ">=2.0.0 <3.0.0", - available: true, - }, - { - name: "@vue/compiler-sfc", - version: ">=3.0.0 <4.0.0", - available: true, - }, - ]); + const lTranspilers = getAvailableTranspilers(); + deepEqual(Array.isArray(lTranspilers), true); + deepEqual(lTranspilers.length > 0, true); + + lTranspilers.forEach((pTranspiler) => { + deepEqual( + typeof pTranspiler.name, + "string", + `name is not a string: ${pTranspiler.name}`, + ); + deepEqual( + typeof pTranspiler.version, + "string", + `version is not a string: ${pTranspiler.version}`, + ); + deepEqual( + validRange(pTranspiler.version), + pTranspiler.version, + `version is not a valid semver range: ${pTranspiler.version}`, + ); + deepEqual( + typeof pTranspiler.currentVersion, + "string", + `version is not a string: ${pTranspiler.version}`, + ); + deepEqual( + typeof pTranspiler.available, + "boolean", + `available is not a boolean: ${pTranspiler.available}`, + ); + // eslint-disable-next-line no-magic-numbers + deepEqual(Object.keys(pTranspiler).length, 4); + }); }); });