dependency-cruiser 16.7.0-beta-2 → 16.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-cruiser",
3
- "version": "16.7.0-beta-2",
3
+ "version": "16.7.0",
4
4
  "description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
5
5
  "keywords": [
6
6
  "static analysis",
@@ -62,6 +62,17 @@ function buildTsPreCompilationDepsAttribute(pInitOptions) {
62
62
  : "// tsPreCompilationDeps: false,";
63
63
  }
64
64
 
65
+ /**
66
+ *
67
+ * @param {IInitConfig} pInitOptions
68
+ * @returns {string}
69
+ */
70
+ function buildDetectJSDocumentImportsAttribute(pInitOptions) {
71
+ return pInitOptions.detectJSDocImports
72
+ ? "detectJSDocImports: true,"
73
+ : "// detectJSDocImports: true,";
74
+ }
75
+
65
76
  /**
66
77
  * @param {IInitConfig} pInitOptions
67
78
  * @returns {string}
@@ -168,6 +179,10 @@ export default function buildConfig(pInitOptions) {
168
179
  extensionsToString(pInitOptions.resolutionExtensions),
169
180
  )
170
181
  .replace("{{notToTestRule}}", buildNotToTestRule(pInitOptions))
182
+ .replace(
183
+ "{{detectJSDocImportsAttribute}}",
184
+ buildDetectJSDocumentImportsAttribute(pInitOptions),
185
+ )
171
186
  .replace(
172
187
  "{{tsPreCompilationDepsAttribute}}",
173
188
  buildTsPreCompilationDepsAttribute(pInitOptions),
@@ -229,7 +229,7 @@ module.exports = {
229
229
  // moduleSystems: ['cjs', 'es6'],
230
230
 
231
231
  /*
232
- false: don't look at JSDoc imports
232
+ false: don't look at JSDoc imports (the default)
233
233
  true: dependency-cruiser will detect dependencies in JSDoc-style
234
234
  import statements. Implies "parser": "tsc", so the dependency-cruiser
235
235
  will use the typescript parser for JavaScript files.
@@ -237,7 +237,7 @@ module.exports = {
237
237
  For this to work the typescript compiler will need to be installed in the
238
238
  same spot as you're running dependency-cruiser from.
239
239
  */
240
- // detectJSDocImports: true,
240
+ {{detectJSDocImportsAttribute}}
241
241
 
242
242
  /* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/main/'
243
243
  to open it on your online repo or \`vscode://file/$\{process.cwd()}/\` to
@@ -18,6 +18,7 @@ import {
18
18
  getWebpackConfigCandidates,
19
19
  } from "./environment-helpers.mjs";
20
20
  import { validateLocation } from "./validators.mjs";
21
+ import { isAvailable as tscIsAvailable } from "#extract/tsc/parse.mjs";
21
22
 
22
23
  function toPromptChoice(pString) {
23
24
  return {
@@ -108,6 +109,12 @@ const QUESTIONS = [
108
109
  message: "Full path to your 'tsconfig.json",
109
110
  choices: getTSConfigCandidates().map(toPromptChoice),
110
111
  },
112
+ {
113
+ name: "detectJSDocImports",
114
+ type: () => (tscIsAvailable() ? "confirm" : false),
115
+ message: "Do you want to detect JSDoc imports as well (slower)?",
116
+ initial: false,
117
+ },
111
118
  {
112
119
  name: "tsPreCompilationDeps",
113
120
  type: (_, pAnswers) => (pAnswers.useTsConfig ? "confirm" : false),
@@ -43,6 +43,7 @@ function getOneShotConfig(pOneShotConfigId) {
43
43
  jsConfig: getJSConfigCandidates().shift(),
44
44
  useTsConfig: hasTSConfigCandidates(),
45
45
  tsConfig: getTSConfigCandidates().shift(),
46
+ detectJSDocImports: false,
46
47
  tsPreCompilationDeps: hasTSConfigCandidates(),
47
48
  useWebpackConfig: hasWebpackConfigCandidates(),
48
49
  webpackConfig: getWebpackConfigCandidates().shift(),
@@ -56,11 +56,6 @@ const EXPERIMENTAL_SCRIPT_DOC = [
56
56
  EOL +
57
57
  " to stdout - in simple text",
58
58
  },
59
- // {
60
- // name: "depcruise:text",
61
- // headline: "",
62
- // description: "",
63
- // },
64
59
  ];
65
60
 
66
61
  /**
@@ -2,17 +2,19 @@ import enhancedResolve from "enhanced-resolve";
2
2
  import { stripQueryParameters } from "../helpers.mjs";
3
3
  import pathToPosix from "#utl/path-to-posix.mjs";
4
4
 
5
- let gResolvers = {};
6
- let gInitialized = {};
5
+ let gResolvers = new Map();
6
+ let gInitialized = new Map();
7
7
 
8
8
  function init(pEHResolveOptions, pCachingContext) {
9
- if (!gInitialized[pCachingContext] || pEHResolveOptions.bustTheCache) {
9
+ if (!gInitialized.get(pCachingContext) || pEHResolveOptions.bustTheCache) {
10
10
  // assuming the cached file system here
11
11
  pEHResolveOptions.fileSystem.purge();
12
- gResolvers[pCachingContext] =
13
- enhancedResolve.ResolverFactory.createResolver(pEHResolveOptions);
12
+ gResolvers.set(
13
+ pCachingContext,
14
+ enhancedResolve.ResolverFactory.createResolver(pEHResolveOptions),
15
+ );
14
16
  /* eslint security/detect-object-injection:0 */
15
- gInitialized[pCachingContext] = true;
17
+ gInitialized.set(pCachingContext, true);
16
18
  }
17
19
  }
18
20
 
@@ -35,7 +37,7 @@ export function resolve(
35
37
  init(pResolveOptions, pCachingContext);
36
38
 
37
39
  return stripQueryParameters(
38
- gResolvers[pCachingContext].resolveSync(
40
+ gResolvers.get(pCachingContext).resolveSync(
39
41
  {},
40
42
  // lookupStartPath
41
43
  pathToPosix(pFileDirectory),
@@ -46,6 +48,6 @@ export function resolve(
46
48
  }
47
49
 
48
50
  export function clearCache() {
49
- gInitialized = {};
50
- gResolvers = {};
51
+ gInitialized.clear();
52
+ gResolvers.clear();
51
53
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable security/detect-object-injection */
1
2
  /* eslint-disable unicorn/prevent-abbreviations */
2
3
  /* eslint-disable max-lines */
3
4
  /* eslint-disable no-inline-comments */
@@ -263,7 +264,7 @@ function extractJSDocImportTags(pJSDocTags) {
263
264
  .filter(
264
265
  (pTag) =>
265
266
  pTag.tagName.escapedText === "import" &&
266
- pTag.moduleSpecifier?.kind &&
267
+ pTag.moduleSpecifier &&
267
268
  typescript.SyntaxKind[pTag.moduleSpecifier.kind] === "StringLiteral" &&
268
269
  pTag.moduleSpecifier.text,
269
270
  )
@@ -275,26 +276,64 @@ function extractJSDocImportTags(pJSDocTags) {
275
276
  }));
276
277
  }
277
278
 
279
+ function isJSDocImport(pTypeNode) {
280
+ // import('./hello.mjs') within jsdoc
281
+ return (
282
+ typescript.SyntaxKind[pTypeNode?.kind] === "LastTypeNode" &&
283
+ typescript.SyntaxKind[pTypeNode.argument?.kind] === "LiteralType" &&
284
+ typescript.SyntaxKind[pTypeNode.argument?.literal?.kind] ===
285
+ "StringLiteral" &&
286
+ pTypeNode.argument.literal.text
287
+ );
288
+ }
289
+
290
+ function keyInJSDocIsIgnorable(pKey) {
291
+ return [
292
+ "parent",
293
+ "pos",
294
+ "end",
295
+ "flags",
296
+ "emitNode",
297
+ "modifierFlagsCache",
298
+ "transformFlags",
299
+ "id",
300
+ "flowNode",
301
+ "symbol",
302
+ "original",
303
+ ].includes(pKey);
304
+ }
305
+
306
+ export function walkJSDoc(pObject, pCollection = new Set()) {
307
+ if (isJSDocImport(pObject)) {
308
+ pCollection.add(pObject.argument.literal.text);
309
+ } else if (Array.isArray(pObject)) {
310
+ pObject.forEach((pValue) => walkJSDoc(pValue, pCollection));
311
+ } else if (typeof pObject === "object") {
312
+ for (const lKey in pObject) {
313
+ if (!keyInJSDocIsIgnorable(lKey) && pObject[lKey]) {
314
+ walkJSDoc(pObject[lKey], pCollection);
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ export function getJSDocImports(pTagNode) {
321
+ const lCollection = new Set();
322
+ walkJSDoc(pTagNode, lCollection);
323
+ return Array.from(lCollection);
324
+ }
325
+
278
326
  function extractJSDocBracketImports(pJSDocTags) {
327
+ // https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
279
328
  return pJSDocTags
280
329
  .filter(
281
330
  (pTag) =>
282
331
  pTag.tagName.escapedText !== "import" &&
283
- /* c8 ignore start */
284
- typescript.SyntaxKind[pTag.typeExpression?.kind ?? -1] ===
285
- "FirstJSDocNode" &&
286
- typescript.SyntaxKind[pTag.typeExpression.type?.kind ?? -1] ===
287
- "LastTypeNode" &&
288
- typescript.SyntaxKind[pTag.typeExpression.type.argument?.kind ?? -1] ===
289
- "LiteralType" &&
290
- typescript.SyntaxKind[
291
- pTag.typeExpression.type.argument?.literal?.kind ?? -1
292
- ] === "StringLiteral" &&
293
- /* c8 ignore stop*/
294
- pTag.typeExpression.type.argument.literal.text,
332
+ typescript.SyntaxKind[pTag.typeExpression?.kind] === "FirstJSDocNode",
295
333
  )
296
- .map((pTag) => ({
297
- module: pTag.typeExpression.type.argument.literal.text,
334
+ .flatMap((pTag) => getJSDocImports(pTag))
335
+ .map((pImportName) => ({
336
+ module: pImportName,
298
337
  moduleSystem: "es6",
299
338
  exoticallyRequired: false,
300
339
  dependencyTypes: ["type-only", "import", "jsdoc", "jsdoc-bracket-import"],
@@ -302,6 +341,7 @@ function extractJSDocBracketImports(pJSDocTags) {
302
341
  }
303
342
 
304
343
  function extractJSDocImports(pJSDocNodes) {
344
+ // https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-jsdoc-import-tag
305
345
  const lJSDocNodesWithTags = pJSDocNodes.filter(
306
346
  (pJSDocLine) => pJSDocLine.tags,
307
347
  );
@@ -371,14 +411,8 @@ function walk(pResult, pExoticRequireStrings, pDetectJSDocImports) {
371
411
  });
372
412
  }
373
413
 
374
- // /** @import thing from './module' */
375
- // /** @import {thing} from './module' */
376
- // /** @import * as thing from './module' */
377
- // see https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-jsdoc-import-tag
378
- //
379
- // TODO: all the kinds of tags that can have import statements as type declarations
380
- // (e.g. @type, @param, @returns, @typedef, @property, @prop, @arg, ...)
381
- // https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
414
+ // /** @import thing from './module' */ etc
415
+ // /** @type {import('module').thing}*/ etc
382
416
  if (pDetectJSDocImports && pASTNode.jsDoc) {
383
417
  const lJSDocImports = extractJSDocImports(pASTNode.jsDoc);
384
418
 
package/src/meta.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /* generated - don't edit */
2
2
 
3
3
  module.exports = {
4
- version: "16.7.0-beta-2",
4
+ version: "16.7.0",
5
5
  engines: {
6
6
  node: "^18.17||>=20",
7
7
  },