From ff15459106ef041397bce504ee3b1407c7b384fc Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Tue, 27 Aug 2024 21:54:37 +0100 Subject: [PATCH 01/14] Handle modules outside module root --- index.js | 67 +++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/index.js b/index.js index d9e8174..f5db119 100644 --- a/index.js +++ b/index.js @@ -7,12 +7,12 @@ const addInherited = require('jsdoc/augment').addInherited; // eslint-disable-li const config = env.conf.typescript; if (!config) { throw new Error( - 'Configuration "typescript" for jsdoc-plugin-typescript missing.', + 'Configuration "typescript" for jsdoc-plugin-typescript missing.' ); } if (!('moduleRoot' in config)) { throw new Error( - 'Configuration "typescript.moduleRoot" for jsdoc-plugin-typescript missing.', + 'Configuration "typescript.moduleRoot" for jsdoc-plugin-typescript missing.' ); } const moduleRoot = config.moduleRoot; @@ -21,7 +21,7 @@ if (!fs.existsSync(moduleRootAbsolute)) { throw new Error( 'Directory "' + moduleRootAbsolute + - '" does not exist. Check the "typescript.moduleRoot" config option for jsdoc-plugin-typescript', + '" does not exist. Check the "typescript.moduleRoot" config option for jsdoc-plugin-typescript' ); } @@ -31,6 +31,7 @@ const typedefRegEx = /@typedef \{[^\}]*\} (\S+)/g; const noClassdescRegEx = /@(typedef|module|type)/; const extensionReplaceRegEx = /\.m?js$/; const slashRegEx = /\\/g; +const leadingPathSegmentRegEx = /^(.?.[/\\])+/; const moduleInfos = {}; const fileNodes = {}; @@ -44,11 +45,7 @@ function getExtension(absolutePath) { function getModuleInfo(moduleId, extension, parser) { if (!moduleInfos[moduleId]) { if (!fileNodes[moduleId]) { - const absolutePath = path.join( - process.cwd(), - moduleRoot, - moduleId + extension, - ); + const absolutePath = path.join(moduleRootAbsolute, moduleId + extension); if (!fs.existsSync(absolutePath)) { return null; } @@ -91,6 +88,13 @@ function getDelimiter(moduleId, symbol, parser) { return getModuleInfo(moduleId, parser).namedExports[symbol] ? '.' : '~'; } +function getModuleId(modulePath) { + return path + .relative(moduleRootAbsolute, modulePath) + .replace(extensionReplaceRegEx, '') + .replace(leadingPathSegmentRegEx, ''); +} + exports.defineTags = function (dictionary) { ['type', 'typedef', 'property', 'return', 'param', 'template'].forEach( function (tagName) { @@ -135,17 +139,14 @@ exports.defineTags = function (dictionary) { } throw new Error("Missing closing '}'"); }; - }, + } ); }; exports.astNodeVisitor = { visitNode: function (node, e, parser, currentSourceName) { if (node.type === 'File') { - const modulePath = path - .relative(path.join(process.cwd(), moduleRoot), currentSourceName) - .replace(extensionReplaceRegEx, ''); - fileNodes[modulePath] = node; + fileNodes[getModuleId(currentSourceName)] = node; const identifiers = {}; if (node.program && node.program.body) { const nodes = node.program.body; @@ -229,10 +230,10 @@ exports.astNodeVisitor = { if ( leadingComments.length === 0 || (leadingComments[leadingComments.length - 1].value.indexOf( - '@classdesc', + '@classdesc' ) === -1 && noClassdescRegEx.test( - leadingComments[leadingComments.length - 1].value, + leadingComments[leadingComments.length - 1].value )) ) { // Create a suitable comment node if we don't have one on the class yet @@ -250,7 +251,7 @@ exports.astNodeVisitor = { if (node.superClass) { // Remove the `@extends` tag because JSDoc does not does not handle generic type. (`@extends {Base}`) const extendsIndex = lines.findIndex((line) => - line.includes('@extends'), + line.includes('@extends') ); if (extendsIndex !== -1) { lines.splice(extendsIndex, 1); @@ -262,13 +263,12 @@ exports.astNodeVisitor = { if (identifier) { const absolutePath = path.resolve( path.dirname(currentSourceName), - identifier.value, + identifier.value ); // default to js extension since .js extention is assumed implicitly const extension = getExtension(absolutePath); - const moduleId = path - .relative(path.join(process.cwd(), moduleRoot), absolutePath) - .replace(extensionReplaceRegEx, ''); + const moduleId = getModuleId(absolutePath); + if (getModuleInfo(moduleId, extension, parser)) { const exportName = identifier.defaultImport ? getDefaultExportName(moduleId, parser) @@ -295,7 +295,7 @@ exports.astNodeVisitor = { // Replace typeof Foo with Class comment.value = comment.value.replace( /typeof ([^,\|\}\>]*)([,\|\}\>])/g, - 'Class<$1>$2', + 'Class<$1>$2' ); // Remove `@override` annotations to avoid JSDoc breaking the inheritance chain @@ -323,7 +323,7 @@ exports.astNodeVisitor = { if (replaceAttempt > 100) { // infinite loop protection throw new Error( - `Invalid docstring ${comment.value} in ${currentSourceName}.`, + `Invalid docstring ${comment.value} in ${currentSourceName}.` ); } } else { @@ -332,13 +332,12 @@ exports.astNodeVisitor = { lastImportPath = importExpression; const rel = path.resolve( path.dirname(currentSourceName), - importSource, + importSource ); // default to js extension since .js extention is assumed implicitly const extension = getExtension(rel); - const moduleId = path - .relative(path.join(process.cwd(), moduleRoot), rel) - .replace(extensionReplaceRegEx, ''); + const moduleId = getModuleId(rel); + if (getModuleInfo(moduleId, extension, parser)) { const name = exportName === 'default' @@ -356,7 +355,7 @@ exports.astNodeVisitor = { if (replacement) { comment.value = comment.value.replace( importExpression, - replacement + remainder, + replacement + remainder ); } } @@ -377,13 +376,13 @@ exports.astNodeVisitor = { Object.keys(identifiers).forEach((key) => { const eventRegex = new RegExp( `@(event |fires )${key}([^A-Za-z])`, - 'g', + 'g' ); replace(eventRegex); const typeRegex = new RegExp( `@(.*[{<|,(!?:]\\s*)${key}([^A-Za-z].*?\}|\})`, - 'g', + 'g' ); replace(typeRegex); @@ -392,13 +391,11 @@ exports.astNodeVisitor = { const identifier = identifiers[key]; const absolutePath = path.resolve( path.dirname(currentSourceName), - identifier.value, + identifier.value ); // default to js extension since .js extention is assumed implicitly const extension = getExtension(absolutePath); - const moduleId = path - .relative(path.join(process.cwd(), moduleRoot), absolutePath) - .replace(extensionReplaceRegEx, ''); + const moduleId = getModuleId(absolutePath); if (getModuleInfo(moduleId, extension, parser)) { const exportName = identifier.defaultImport ? getDefaultExportName(moduleId, parser) @@ -408,11 +405,11 @@ exports.astNodeVisitor = { : getDelimiter(moduleId, exportName, parser); const replacement = `module:${moduleId.replace( slashRegEx, - '/', + '/' )}${exportName ? delimiter + exportName : ''}`; comment.value = comment.value.replace( regex, - '@$1' + replacement + '$2', + '@$1' + replacement + '$2' ); } } From c6bafeacfd1997db5bec8cbd66d46eddcfb816c0 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:42:05 +0100 Subject: [PATCH 02/14] Support explicit and inferred module ids --- index.js | 170 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 61 deletions(-) diff --git a/index.js b/index.js index f5db119..a0482de 100644 --- a/index.js +++ b/index.js @@ -4,20 +4,13 @@ const fs = require('fs'); const env = require('jsdoc/env'); // eslint-disable-line import/no-unresolved const addInherited = require('jsdoc/augment').addInherited; // eslint-disable-line import/no-unresolved -const config = env.conf.typescript; -if (!config) { - throw new Error( - 'Configuration "typescript" for jsdoc-plugin-typescript missing.' - ); -} -if (!('moduleRoot' in config)) { - throw new Error( - 'Configuration "typescript.moduleRoot" for jsdoc-plugin-typescript missing.' - ); -} -const moduleRoot = config.moduleRoot; -const moduleRootAbsolute = path.join(process.cwd(), moduleRoot); -if (!fs.existsSync(moduleRootAbsolute)) { +const config = env.conf; +const moduleRoot = config.typescript ? config.typescript.moduleRoot : undefined; +const moduleRootAbsolute = moduleRoot + ? path.join(process.cwd(), moduleRoot) + : undefined; + +if (moduleRootAbsolute && !fs.existsSync(moduleRootAbsolute)) { throw new Error( 'Directory "' + moduleRootAbsolute + @@ -30,31 +23,55 @@ const importRegEx = const typedefRegEx = /@typedef \{[^\}]*\} (\S+)/g; const noClassdescRegEx = /@(typedef|module|type)/; const extensionReplaceRegEx = /\.m?js$/; +const extensionEnsureRegEx = /(\.js)?$/; const slashRegEx = /\\/g; const leadingPathSegmentRegEx = /^(.?.[/\\])+/; const moduleInfos = {}; const fileNodes = {}; -function getExtension(absolutePath) { - return extensionReplaceRegEx.test(absolutePath) - ? extensionReplaceRegEx.exec(absolutePath)[0] - : '.js'; +let inferredModuleRoot; + +function getInferredModuleRoot() { + if (inferredModuleRoot) { + return inferredModuleRoot; + } + + inferredModuleRoot = env.sourceFiles.reduce((nearestAncestor, curr, i) => { + if (curr.startsWith(nearestAncestor) || i === 0) { + return nearestAncestor; + } + + const currParts = curr.split(path.sep); + const nearestParts = nearestAncestor.split(path.sep); + + for (let i = 0; i < currParts.length; ++i) { + if (currParts[i] !== nearestParts[i]) { + return currParts.slice(0, i).join(path.sep); + } + } + }, path.dirname(env.sourceFiles[0])); + + return inferredModuleRoot; } -function getModuleInfo(moduleId, extension, parser) { - if (!moduleInfos[moduleId]) { - if (!fileNodes[moduleId]) { - const absolutePath = path.join(moduleRootAbsolute, moduleId + extension); - if (!fs.existsSync(absolutePath)) { +function getModuleInfo(modulePath, parser) { + if (!moduleInfos[modulePath]) { + if (!fileNodes[modulePath]) { + if (!fs.existsSync(modulePath)) { return null; } - const file = fs.readFileSync(absolutePath, 'UTF-8'); - fileNodes[moduleId] = parser.astBuilder.build(file, absolutePath); + + const file = fs.readFileSync(modulePath, 'UTF-8'); + + fileNodes[modulePath] = parser.astBuilder.build(file, modulePath); } - moduleInfos[moduleId] = {namedExports: {}}; - const moduleInfo = moduleInfos[moduleId]; - const node = fileNodes[moduleId]; + + moduleInfos[modulePath] = {namedExports: {}}; + + const moduleInfo = moduleInfos[modulePath]; + const node = fileNodes[modulePath]; + if (node.program && node.program.body) { const classDeclarations = {}; const nodes = node.program.body; @@ -77,22 +94,57 @@ function getModuleInfo(moduleId, extension, parser) { } } } - return moduleInfos[moduleId]; + + return moduleInfos[modulePath]; } -function getDefaultExportName(moduleId, parser) { - return getModuleInfo(moduleId, parser).defaultExport; +function getDefaultExportName(modulePath) { + return getModuleInfo(modulePath).defaultExport; } -function getDelimiter(moduleId, symbol, parser) { - return getModuleInfo(moduleId, parser).namedExports[symbol] ? '.' : '~'; +function getDelimiter(modulePath, symbol) { + return getModuleInfo(modulePath).namedExports[symbol] ? '.' : '~'; } function getModuleId(modulePath) { - return path - .relative(moduleRootAbsolute, modulePath) - .replace(extensionReplaceRegEx, '') - .replace(leadingPathSegmentRegEx, ''); + // Use moduleRoot if set + if (moduleRootAbsolute) { + return path + .relative(moduleRootAbsolute, modulePath) + .replace(extensionReplaceRegEx, '') + .replace(leadingPathSegmentRegEx, ''); + } + + // Search for explicit module id + if (fileNodes[modulePath]) { + for (const comment of fileNodes[modulePath].comments) { + if (!/@module(?=\s)/.test(comment.value)) { + continue; + } + + const explicitModuleId = comment.value + .split(/@module(?=\s)/)[1] + .split(/\n+\s*\*\s*@\w+/)[0] // Split before the next tag + .replace(/\n+\s*\*|\{[^\}]*\}/g, '') // Remove new lines with asterisks, and type annotations + .trim(); + + if (explicitModuleId) { + return explicitModuleId; + } + } + } + + if (getInferredModuleRoot()) { + return path + .relative(inferredModuleRoot, modulePath) + .replace(extensionReplaceRegEx, ''); + } + + throw new Error(`Unable to resolve module id for file ${modulePath}.`); +} + +function withJsExt(filePath) { + return filePath.replace(extensionEnsureRegEx, '.js'); } exports.defineTags = function (dictionary) { @@ -146,7 +198,7 @@ exports.defineTags = function (dictionary) { exports.astNodeVisitor = { visitNode: function (node, e, parser, currentSourceName) { if (node.type === 'File') { - fileNodes[getModuleId(currentSourceName)] = node; + fileNodes[currentSourceName] = node; const identifiers = {}; if (node.program && node.program.body) { const nodes = node.program.body; @@ -263,19 +315,18 @@ exports.astNodeVisitor = { if (identifier) { const absolutePath = path.resolve( path.dirname(currentSourceName), - identifier.value + withJsExt(identifier.value) ); - // default to js extension since .js extention is assumed implicitly - const extension = getExtension(absolutePath); - const moduleId = getModuleId(absolutePath); - if (getModuleInfo(moduleId, extension, parser)) { + if (getModuleInfo(absolutePath, parser)) { + const moduleId = getModuleId(absolutePath); + const exportName = identifier.defaultImport - ? getDefaultExportName(moduleId, parser) + ? getDefaultExportName(absolutePath) : node.superClass.name; const delimiter = identifier.defaultImport ? '~' - : getDelimiter(moduleId, exportName, parser); + : getDelimiter(absolutePath, exportName); lines[lines.length - 2] = ' * @extends ' + `module:${moduleId.replace(slashRegEx, '/')}${ @@ -332,21 +383,18 @@ exports.astNodeVisitor = { lastImportPath = importExpression; const rel = path.resolve( path.dirname(currentSourceName), - importSource + withJsExt(importSource) ); - // default to js extension since .js extention is assumed implicitly - const extension = getExtension(rel); - const moduleId = getModuleId(rel); - if (getModuleInfo(moduleId, extension, parser)) { + if (getModuleInfo(rel, parser)) { + const moduleId = getModuleId(rel); + const name = exportName === 'default' - ? getDefaultExportName(moduleId, parser) + ? getDefaultExportName(rel) : exportName; const delimiter = - exportName === 'default' - ? '~' - : getDelimiter(moduleId, name, parser); + exportName === 'default' ? '~' : getDelimiter(rel, name); replacement = `module:${moduleId.replace(slashRegEx, '/')}${ name ? delimiter + name : '' }`; @@ -391,18 +439,18 @@ exports.astNodeVisitor = { const identifier = identifiers[key]; const absolutePath = path.resolve( path.dirname(currentSourceName), - identifier.value + withJsExt(identifier.value) ); - // default to js extension since .js extention is assumed implicitly - const extension = getExtension(absolutePath); - const moduleId = getModuleId(absolutePath); - if (getModuleInfo(moduleId, extension, parser)) { + + if (getModuleInfo(absolutePath, parser)) { + const moduleId = getModuleId(absolutePath); + const exportName = identifier.defaultImport - ? getDefaultExportName(moduleId, parser) + ? getDefaultExportName(absolutePath) : key; const delimiter = identifier.defaultImport ? '~' - : getDelimiter(moduleId, exportName, parser); + : getDelimiter(absolutePath, exportName); const replacement = `module:${moduleId.replace( slashRegEx, '/' From b224c265d83007506587e67d9e0a5e33002303b3 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Fri, 30 Aug 2024 23:48:50 +0100 Subject: [PATCH 03/14] Change terminology from inferred to implicit --- index.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index a0482de..d714cd0 100644 --- a/index.js +++ b/index.js @@ -30,14 +30,15 @@ const leadingPathSegmentRegEx = /^(.?.[/\\])+/; const moduleInfos = {}; const fileNodes = {}; -let inferredModuleRoot; +// Without explicit module ids, JSDoc will use the nearest shared ancestor directory +let implicitModuleRoot; -function getInferredModuleRoot() { - if (inferredModuleRoot) { - return inferredModuleRoot; +function getImplicitModuleRoot() { + if (implicitModuleRoot) { + return implicitModuleRoot; } - inferredModuleRoot = env.sourceFiles.reduce((nearestAncestor, curr, i) => { + implicitModuleRoot = env.sourceFiles.reduce((nearestAncestor, curr, i) => { if (curr.startsWith(nearestAncestor) || i === 0) { return nearestAncestor; } @@ -52,7 +53,7 @@ function getInferredModuleRoot() { } }, path.dirname(env.sourceFiles[0])); - return inferredModuleRoot; + return implicitModuleRoot; } function getModuleInfo(modulePath, parser) { @@ -134,9 +135,9 @@ function getModuleId(modulePath) { } } - if (getInferredModuleRoot()) { + if (getImplicitModuleRoot()) { return path - .relative(inferredModuleRoot, modulePath) + .relative(implicitModuleRoot, modulePath) .replace(extensionReplaceRegEx, ''); } From c6b98a66d242416251f9200cf108affee97653b3 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:03:39 +0100 Subject: [PATCH 04/14] Default to cwd if no source files --- index.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index d714cd0..bf998af 100644 --- a/index.js +++ b/index.js @@ -31,6 +31,7 @@ const moduleInfos = {}; const fileNodes = {}; // Without explicit module ids, JSDoc will use the nearest shared ancestor directory +/** @type {string} */ let implicitModuleRoot; function getImplicitModuleRoot() { @@ -38,20 +39,26 @@ function getImplicitModuleRoot() { return implicitModuleRoot; } - implicitModuleRoot = env.sourceFiles.reduce((nearestAncestor, curr, i) => { - if (curr.startsWith(nearestAncestor) || i === 0) { - return nearestAncestor; + if (!env.sourceFiles || env.sourceFiles.length === 0) { + return process.cwd(); + } + + implicitModuleRoot = env.sourceFiles[0]; + + env.sourceFiles.slice(1).forEach((filePath) => { + if (filePath.startsWith(implicitModuleRoot)) { + return implicitModuleRoot; } - const currParts = curr.split(path.sep); - const nearestParts = nearestAncestor.split(path.sep); + const currParts = filePath.split(path.sep); + const nearestParts = implicitModuleRoot.split(path.sep); for (let i = 0; i < currParts.length; ++i) { if (currParts[i] !== nearestParts[i]) { return currParts.slice(0, i).join(path.sep); } } - }, path.dirname(env.sourceFiles[0])); + }); return implicitModuleRoot; } @@ -135,13 +142,9 @@ function getModuleId(modulePath) { } } - if (getImplicitModuleRoot()) { - return path - .relative(implicitModuleRoot, modulePath) - .replace(extensionReplaceRegEx, ''); - } - - throw new Error(`Unable to resolve module id for file ${modulePath}.`); + return path + .relative(getImplicitModuleRoot(), modulePath) + .replace(extensionReplaceRegEx, ''); } function withJsExt(filePath) { From 980a621db8189ea622b69a3f4994809fb0db766f Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:06:26 +0100 Subject: [PATCH 05/14] Update README to reflect new module resolution method --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c4fc051..4e00832 100644 --- a/README.md +++ b/README.md @@ -10,22 +10,24 @@ JSDoc accepts plugins by simply installing their npm package: To configure JSDoc to use the plugin, add the following to the JSDoc configuration file, e.g. `conf.json`: -```json +```jsonc "plugins": [ "jsdoc-plugin-typescript" ], "typescript": { - "moduleRoot": "src" + "moduleRoot": "src" // optional } ``` See http://usejsdoc.org/about-configuring-jsdoc.html for more details on how to configure JSDoc. -In the above snippet, `"src"` is the directory that contains the source files. Inside that directory, each `.js` file needs a `@module` annotation with a path relative to that `"moduleRoot"`, e.g. +If `typescript.moduleRoot` is specified, the plugin will assume module ids are relative to that directory and format them as such. For example, `@type {import("./folder/file").Class}` will be converted to `@type {module:folder/file.Class}`. The file extension is removed along with any leading `../` segments (if the referenced module is outside `moduleRoot`). -```js -/** @module ol/proj **/ -``` +In the absence of `typescript.moduleRoot`, the plugin will mirror the method JSDoc uses to assign module ids: + +1. Parse the referenced module for an `@module` tag. +2. If a tag is found and it has an explicit id, use that. +3. If a tag is found, but it doesn't have an explicit id, use the file path relative to the **nearest shared parent directory**, and remove the file extension. ## What this plugin does From 2033987398f87bce8276e71ca0b26ea04ca9899c Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:31:10 +0100 Subject: [PATCH 06/14] Fix getImplicitModuleRoot --- index.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index bf998af..4bfc5aa 100644 --- a/index.js +++ b/index.js @@ -30,10 +30,13 @@ const leadingPathSegmentRegEx = /^(.?.[/\\])+/; const moduleInfos = {}; const fileNodes = {}; -// Without explicit module ids, JSDoc will use the nearest shared ancestor directory +// Without explicit module ids, JSDoc will use the nearest shared parent directory /** @type {string} */ let implicitModuleRoot; +/** + * @return {string} The nearest shared parent directory of all source files. + */ function getImplicitModuleRoot() { if (implicitModuleRoot) { return implicitModuleRoot; @@ -43,11 +46,11 @@ function getImplicitModuleRoot() { return process.cwd(); } - implicitModuleRoot = env.sourceFiles[0]; + implicitModuleRoot = path.dirname(env.sourceFiles[0]); env.sourceFiles.slice(1).forEach((filePath) => { if (filePath.startsWith(implicitModuleRoot)) { - return implicitModuleRoot; + return; } const currParts = filePath.split(path.sep); @@ -55,7 +58,9 @@ function getImplicitModuleRoot() { for (let i = 0; i < currParts.length; ++i) { if (currParts[i] !== nearestParts[i]) { - return currParts.slice(0, i).join(path.sep); + implicitModuleRoot = currParts.slice(0, i).join(path.sep); + + return; } } }); From d9623d9df6e1f80cc6f912a74713d558873f26ff Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:48:20 +0100 Subject: [PATCH 07/14] Use process.cwd as root dir if all source files are within it --- README.md | 4 +++- index.js | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4e00832..1aa287c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ In the absence of `typescript.moduleRoot`, the plugin will mirror the method JSD 1. Parse the referenced module for an `@module` tag. 2. If a tag is found and it has an explicit id, use that. -3. If a tag is found, but it doesn't have an explicit id, use the file path relative to the **nearest shared parent directory**, and remove the file extension. +3. If a tag is found, but it doesn't have an explicit id, use the module's file path relative to the root directory, and remove the file extension. + +**NOTE:** The root directory will be `process.cwd()` unless any source files are outside it, in which case, the root folder is considered the nearest shared parent directory of all source files. ## What this plugin does diff --git a/index.js b/index.js index 4bfc5aa..adf51ca 100644 --- a/index.js +++ b/index.js @@ -35,17 +35,25 @@ const fileNodes = {}; let implicitModuleRoot; /** - * @return {string} The nearest shared parent directory of all source files. + * Without explicit module ids, JSDoc will use `process.cwd()` if all source files are within cwd. + * If any source files are outside cwd, JSDoc will use the nearest shared parent directory. + * @return {string} The implicit root path with which to resolve all module ids against. */ function getImplicitModuleRoot() { if (implicitModuleRoot) { return implicitModuleRoot; } - if (!env.sourceFiles || env.sourceFiles.length === 0) { + if ( + !env.sourceFiles || + env.sourceFiles.length === 0 || + // If all files are in cwd + env.sourceFiles.every((f) => f.startsWith(process.cwd())) + ) { return process.cwd(); } + // Find the nearest shared parent directory implicitModuleRoot = path.dirname(env.sourceFiles[0]); env.sourceFiles.slice(1).forEach((filePath) => { From 47cf7c35bc3144401ca59cb61c612c384eb85400 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:10:10 +0100 Subject: [PATCH 08/14] Cache module id with module info --- index.js | 77 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/index.js b/index.js index adf51ca..5a21645 100644 --- a/index.js +++ b/index.js @@ -76,6 +76,39 @@ function getImplicitModuleRoot() { return implicitModuleRoot; } +function getModuleId(modulePath) { + // Use moduleRoot if set + if (moduleRootAbsolute) { + return path + .relative(moduleRootAbsolute, modulePath) + .replace(extensionReplaceRegEx, '') + .replace(leadingPathSegmentRegEx, ''); + } + + // Search for explicit module id + if (fileNodes[modulePath]) { + for (const comment of fileNodes[modulePath].comments) { + if (!/@module(?=\s)/.test(comment.value)) { + continue; + } + + const explicitModuleId = comment.value + .split(/@module(?=\s)/)[1] + .split(/\n+\s*\*\s*@\w+/)[0] // Split before the next tag + .replace(/\n+\s*\*|\{[^\}]*\}/g, '') // Remove new lines with asterisks, and type annotations + .trim(); + + if (explicitModuleId) { + return explicitModuleId; + } + } + } + + return path + .relative(getImplicitModuleRoot(), modulePath) + .replace(extensionReplaceRegEx, ''); +} + function getModuleInfo(modulePath, parser) { if (!moduleInfos[modulePath]) { if (!fileNodes[modulePath]) { @@ -88,7 +121,10 @@ function getModuleInfo(modulePath, parser) { fileNodes[modulePath] = parser.astBuilder.build(file, modulePath); } - moduleInfos[modulePath] = {namedExports: {}}; + moduleInfos[modulePath] = { + id: getModuleId(modulePath), + namedExports: {}, + }; const moduleInfo = moduleInfos[modulePath]; const node = fileNodes[modulePath]; @@ -127,39 +163,6 @@ function getDelimiter(modulePath, symbol) { return getModuleInfo(modulePath).namedExports[symbol] ? '.' : '~'; } -function getModuleId(modulePath) { - // Use moduleRoot if set - if (moduleRootAbsolute) { - return path - .relative(moduleRootAbsolute, modulePath) - .replace(extensionReplaceRegEx, '') - .replace(leadingPathSegmentRegEx, ''); - } - - // Search for explicit module id - if (fileNodes[modulePath]) { - for (const comment of fileNodes[modulePath].comments) { - if (!/@module(?=\s)/.test(comment.value)) { - continue; - } - - const explicitModuleId = comment.value - .split(/@module(?=\s)/)[1] - .split(/\n+\s*\*\s*@\w+/)[0] // Split before the next tag - .replace(/\n+\s*\*|\{[^\}]*\}/g, '') // Remove new lines with asterisks, and type annotations - .trim(); - - if (explicitModuleId) { - return explicitModuleId; - } - } - } - - return path - .relative(getImplicitModuleRoot(), modulePath) - .replace(extensionReplaceRegEx, ''); -} - function withJsExt(filePath) { return filePath.replace(extensionEnsureRegEx, '.js'); } @@ -336,7 +339,7 @@ exports.astNodeVisitor = { ); if (getModuleInfo(absolutePath, parser)) { - const moduleId = getModuleId(absolutePath); + const moduleId = moduleInfos[absolutePath]; const exportName = identifier.defaultImport ? getDefaultExportName(absolutePath) @@ -404,7 +407,7 @@ exports.astNodeVisitor = { ); if (getModuleInfo(rel, parser)) { - const moduleId = getModuleId(rel); + const moduleId = moduleInfos[rel]; const name = exportName === 'default' @@ -460,7 +463,7 @@ exports.astNodeVisitor = { ); if (getModuleInfo(absolutePath, parser)) { - const moduleId = getModuleId(absolutePath); + const moduleId = moduleInfos[absolutePath]; const exportName = identifier.defaultImport ? getDefaultExportName(absolutePath) From b18436fb8f557ceb1895c73bb7e47db542dd3488 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:13:34 +0100 Subject: [PATCH 09/14] Fix module id variables --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 5a21645..e3d9655 100644 --- a/index.js +++ b/index.js @@ -339,7 +339,7 @@ exports.astNodeVisitor = { ); if (getModuleInfo(absolutePath, parser)) { - const moduleId = moduleInfos[absolutePath]; + const moduleId = moduleInfos[absolutePath].id; const exportName = identifier.defaultImport ? getDefaultExportName(absolutePath) @@ -407,7 +407,7 @@ exports.astNodeVisitor = { ); if (getModuleInfo(rel, parser)) { - const moduleId = moduleInfos[rel]; + const moduleId = moduleInfos[rel].id; const name = exportName === 'default' @@ -463,7 +463,7 @@ exports.astNodeVisitor = { ); if (getModuleInfo(absolutePath, parser)) { - const moduleId = moduleInfos[absolutePath]; + const moduleId = moduleInfos[absolutePath].id; const exportName = identifier.defaultImport ? getDefaultExportName(absolutePath) From 94d35acccaf910419fb31b3784fbe159ec867ee1 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:14:07 +0100 Subject: [PATCH 10/14] Revert process.cwd root default --- README.md | 14 +++++++++++--- index.js | 10 ++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1aa287c..8f5a15f 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,7 @@ In the absence of `typescript.moduleRoot`, the plugin will mirror the method JSD 1. Parse the referenced module for an `@module` tag. 2. If a tag is found and it has an explicit id, use that. -3. If a tag is found, but it doesn't have an explicit id, use the module's file path relative to the root directory, and remove the file extension. - -**NOTE:** The root directory will be `process.cwd()` unless any source files are outside it, in which case, the root folder is considered the nearest shared parent directory of all source files. +3. If a tag is found, but it doesn't have an explicit id, use the module's file path relative to the nearest shared parent directory, and remove the file extension. ## What this plugin does @@ -44,6 +42,7 @@ TypeScript and JSDoc use a different syntax for imported types. This plugin conv ### TypeScript **Named export:** + ```js /** * @type {import("./path/to/module").exportName} @@ -51,6 +50,7 @@ TypeScript and JSDoc use a different syntax for imported types. This plugin conv ``` **Default export:** + ```js /** * @type {import("./path/to/module").default} @@ -58,6 +58,7 @@ TypeScript and JSDoc use a different syntax for imported types. This plugin conv ``` **typeof type:** + ```js /** * @type {typeof import("./path/to/module").exportName} @@ -65,10 +66,12 @@ TypeScript and JSDoc use a different syntax for imported types. This plugin conv ``` **Template literal type** + ```js /** * @type {`static:${dynamic}`} */ +``` **@override annotations** @@ -77,6 +80,7 @@ are removed because they make JSDoc stop inheritance ### JSDoc **Named export:** + ```js /** * @type {module:path/to/module.exportName} @@ -84,6 +88,7 @@ are removed because they make JSDoc stop inheritance ``` **Default export assigned to a variable in the exporting module:** + ```js /** * @type {module:path/to/module~variableOfDefaultExport} @@ -93,6 +98,7 @@ are removed because they make JSDoc stop inheritance This syntax is also used when referring to types of `@typedef`s and `@enum`s. **Anonymous default export:** + ```js /** * @type {module:path/to/module} @@ -100,6 +106,7 @@ This syntax is also used when referring to types of `@typedef`s and `@enum`s. ``` **typeof type:** + ```js /** * @type {Class} @@ -107,6 +114,7 @@ This syntax is also used when referring to types of `@typedef`s and `@enum`s. ``` **Template literal type** + ```js /** * @type {'static:${dynamic}'} diff --git a/index.js b/index.js index e3d9655..6665b96 100644 --- a/index.js +++ b/index.js @@ -35,8 +35,7 @@ const fileNodes = {}; let implicitModuleRoot; /** - * Without explicit module ids, JSDoc will use `process.cwd()` if all source files are within cwd. - * If any source files are outside cwd, JSDoc will use the nearest shared parent directory. + * Without explicit module ids, JSDoc will use the nearest shared parent directory. * @return {string} The implicit root path with which to resolve all module ids against. */ function getImplicitModuleRoot() { @@ -44,12 +43,7 @@ function getImplicitModuleRoot() { return implicitModuleRoot; } - if ( - !env.sourceFiles || - env.sourceFiles.length === 0 || - // If all files are in cwd - env.sourceFiles.every((f) => f.startsWith(process.cwd())) - ) { + if (!env.sourceFiles || env.sourceFiles.length === 0) { return process.cwd(); } From 8228dd1283aa37fcc143ab26f3c04d4dbbc3bbe2 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Thu, 5 Sep 2024 08:48:44 +0100 Subject: [PATCH 11/14] Resolve index paths --- index.js | 87 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index 6665b96..ff84855 100644 --- a/index.js +++ b/index.js @@ -29,6 +29,7 @@ const leadingPathSegmentRegEx = /^(.?.[/\\])+/; const moduleInfos = {}; const fileNodes = {}; +const resolvedPathCache = new Set(); // Without explicit module ids, JSDoc will use the nearest shared parent directory /** @type {string} */ @@ -71,6 +72,10 @@ function getImplicitModuleRoot() { } function getModuleId(modulePath) { + if (moduleInfos[modulePath]) { + return moduleInfos[modulePath].id; + } + // Use moduleRoot if set if (moduleRootAbsolute) { return path @@ -103,13 +108,39 @@ function getModuleId(modulePath) { .replace(extensionReplaceRegEx, ''); } +/** + * Checks for the existence of `modulePath`, and if it doesn't exist, checks for `modulePath/index.js`. + * @param {string} from The path to the module. + * @param {string} to The path to the module. + * @return {string | null} The resolved path or null if it can't be resolved. + */ +function getResolvedPath(from, to) { + let resolvedPath = path.resolve(from, to); + + if (resolvedPathCache.has(resolvedPath) || fs.existsSync(resolvedPath)) { + resolvedPathCache.add(resolvedPath); + + return resolvedPath; + } + + resolvedPath = resolvedPath.replace(extensionEnsureRegEx, '/index.js'); + + if (resolvedPathCache.has(resolvedPath) || fs.existsSync(resolvedPath)) { + resolvedPathCache.add(resolvedPath); + + return resolvedPath; + } + + return null; +} + function getModuleInfo(modulePath, parser) { + if (!modulePath) { + return null; + } + if (!moduleInfos[modulePath]) { if (!fileNodes[modulePath]) { - if (!fs.existsSync(modulePath)) { - return null; - } - const file = fs.readFileSync(modulePath, 'UTF-8'); fileNodes[modulePath] = parser.astBuilder.build(file, modulePath); @@ -157,7 +188,7 @@ function getDelimiter(modulePath, symbol) { return getModuleInfo(modulePath).namedExports[symbol] ? '.' : '~'; } -function withJsExt(filePath) { +function ensureJsExt(filePath) { return filePath.replace(extensionEnsureRegEx, '.js'); } @@ -327,23 +358,24 @@ exports.astNodeVisitor = { lines.push(lines[lines.length - 1]); const identifier = identifiers[node.superClass.name]; if (identifier) { - const absolutePath = path.resolve( + const absolutePath = getResolvedPath( path.dirname(currentSourceName), - withJsExt(identifier.value) + ensureJsExt(identifier.value) ); + const moduleInfo = getModuleInfo(absolutePath, parser); - if (getModuleInfo(absolutePath, parser)) { - const moduleId = moduleInfos[absolutePath].id; - + if (moduleInfo) { const exportName = identifier.defaultImport ? getDefaultExportName(absolutePath) : node.superClass.name; + const delimiter = identifier.defaultImport ? '~' : getDelimiter(absolutePath, exportName); + lines[lines.length - 2] = ' * @extends ' + - `module:${moduleId.replace(slashRegEx, '/')}${ + `module:${moduleInfo.id.replace(slashRegEx, '/')}${ exportName ? delimiter + exportName : '' }`; } @@ -395,23 +427,26 @@ exports.astNodeVisitor = { replaceAttempt = 0; } lastImportPath = importExpression; - const rel = path.resolve( + + const rel = getResolvedPath( path.dirname(currentSourceName), - withJsExt(importSource) + ensureJsExt(importSource) ); + const moduleInfo = getModuleInfo(rel, parser); - if (getModuleInfo(rel, parser)) { - const moduleId = moduleInfos[rel].id; - + if (moduleInfo) { const name = exportName === 'default' ? getDefaultExportName(rel) : exportName; + const delimiter = exportName === 'default' ? '~' : getDelimiter(rel, name); - replacement = `module:${moduleId.replace(slashRegEx, '/')}${ - name ? delimiter + name : '' - }`; + + replacement = `module:${moduleInfo.id.replace( + slashRegEx, + '/' + )}${name ? delimiter + name : ''}`; } } if (replacement) { @@ -451,24 +486,26 @@ exports.astNodeVisitor = { function replace(regex) { if (regex.test(comment.value)) { const identifier = identifiers[key]; - const absolutePath = path.resolve( + const absolutePath = getResolvedPath( path.dirname(currentSourceName), - withJsExt(identifier.value) + ensureJsExt(identifier.value) ); + const moduleInfo = getModuleInfo(absolutePath, parser); - if (getModuleInfo(absolutePath, parser)) { - const moduleId = moduleInfos[absolutePath].id; - + if (moduleInfo) { const exportName = identifier.defaultImport ? getDefaultExportName(absolutePath) : key; + const delimiter = identifier.defaultImport ? '~' : getDelimiter(absolutePath, exportName); - const replacement = `module:${moduleId.replace( + + const replacement = `module:${moduleInfo.id.replace( slashRegEx, '/' )}${exportName ? delimiter + exportName : ''}`; + comment.value = comment.value.replace( regex, '@$1' + replacement + '$2' From 37153d3de2d2eeef4183395ff275a15bce4b22e8 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Thu, 5 Sep 2024 22:58:41 +0100 Subject: [PATCH 12/14] Prettier trailing commas --- index.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index ff84855..29eddef 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ if (moduleRootAbsolute && !fs.existsSync(moduleRootAbsolute)) { throw new Error( 'Directory "' + moduleRootAbsolute + - '" does not exist. Check the "typescript.moduleRoot" config option for jsdoc-plugin-typescript' + '" does not exist. Check the "typescript.moduleRoot" config option for jsdoc-plugin-typescript', ); } @@ -236,7 +236,7 @@ exports.defineTags = function (dictionary) { } throw new Error("Missing closing '}'"); }; - } + }, ); }; @@ -327,10 +327,10 @@ exports.astNodeVisitor = { if ( leadingComments.length === 0 || (leadingComments[leadingComments.length - 1].value.indexOf( - '@classdesc' + '@classdesc', ) === -1 && noClassdescRegEx.test( - leadingComments[leadingComments.length - 1].value + leadingComments[leadingComments.length - 1].value, )) ) { // Create a suitable comment node if we don't have one on the class yet @@ -348,7 +348,7 @@ exports.astNodeVisitor = { if (node.superClass) { // Remove the `@extends` tag because JSDoc does not does not handle generic type. (`@extends {Base}`) const extendsIndex = lines.findIndex((line) => - line.includes('@extends') + line.includes('@extends'), ); if (extendsIndex !== -1) { lines.splice(extendsIndex, 1); @@ -360,7 +360,7 @@ exports.astNodeVisitor = { if (identifier) { const absolutePath = getResolvedPath( path.dirname(currentSourceName), - ensureJsExt(identifier.value) + ensureJsExt(identifier.value), ); const moduleInfo = getModuleInfo(absolutePath, parser); @@ -392,7 +392,7 @@ exports.astNodeVisitor = { // Replace typeof Foo with Class comment.value = comment.value.replace( /typeof ([^,\|\}\>]*)([,\|\}\>])/g, - 'Class<$1>$2' + 'Class<$1>$2', ); // Remove `@override` annotations to avoid JSDoc breaking the inheritance chain @@ -420,7 +420,7 @@ exports.astNodeVisitor = { if (replaceAttempt > 100) { // infinite loop protection throw new Error( - `Invalid docstring ${comment.value} in ${currentSourceName}.` + `Invalid docstring ${comment.value} in ${currentSourceName}.`, ); } } else { @@ -430,7 +430,7 @@ exports.astNodeVisitor = { const rel = getResolvedPath( path.dirname(currentSourceName), - ensureJsExt(importSource) + ensureJsExt(importSource), ); const moduleInfo = getModuleInfo(rel, parser); @@ -445,14 +445,14 @@ exports.astNodeVisitor = { replacement = `module:${moduleInfo.id.replace( slashRegEx, - '/' + '/', )}${name ? delimiter + name : ''}`; } } if (replacement) { comment.value = comment.value.replace( importExpression, - replacement + remainder + replacement + remainder, ); } } @@ -473,13 +473,13 @@ exports.astNodeVisitor = { Object.keys(identifiers).forEach((key) => { const eventRegex = new RegExp( `@(event |fires )${key}([^A-Za-z])`, - 'g' + 'g', ); replace(eventRegex); const typeRegex = new RegExp( `@(.*[{<|,(!?:]\\s*)${key}([^A-Za-z].*?\}|\})`, - 'g' + 'g', ); replace(typeRegex); @@ -488,7 +488,7 @@ exports.astNodeVisitor = { const identifier = identifiers[key]; const absolutePath = getResolvedPath( path.dirname(currentSourceName), - ensureJsExt(identifier.value) + ensureJsExt(identifier.value), ); const moduleInfo = getModuleInfo(absolutePath, parser); @@ -503,12 +503,12 @@ exports.astNodeVisitor = { const replacement = `module:${moduleInfo.id.replace( slashRegEx, - '/' + '/', )}${exportName ? delimiter + exportName : ''}`; comment.value = comment.value.replace( regex, - '@$1' + replacement + '$2' + '@$1' + replacement + '$2', ); } } From f6c9ac56b5cbb63636af41821818bb5eab2a4422 Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Wed, 11 Sep 2024 09:28:31 +0100 Subject: [PATCH 13/14] Remove moduleRoot config option --- README.md | 19 ++++++++----------- index.js | 24 ------------------------ test/template/config.json | 11 ++--------- 3 files changed, 10 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 8f5a15f..326bf62 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,10 @@ To configure JSDoc to use the plugin, add the following to the JSDoc configurati "plugins": [ "jsdoc-plugin-typescript" ], -"typescript": { - "moduleRoot": "src" // optional -} ``` See http://usejsdoc.org/about-configuring-jsdoc.html for more details on how to configure JSDoc. -If `typescript.moduleRoot` is specified, the plugin will assume module ids are relative to that directory and format them as such. For example, `@type {import("./folder/file").Class}` will be converted to `@type {module:folder/file.Class}`. The file extension is removed along with any leading `../` segments (if the referenced module is outside `moduleRoot`). - -In the absence of `typescript.moduleRoot`, the plugin will mirror the method JSDoc uses to assign module ids: - -1. Parse the referenced module for an `@module` tag. -2. If a tag is found and it has an explicit id, use that. -3. If a tag is found, but it doesn't have an explicit id, use the module's file path relative to the nearest shared parent directory, and remove the file extension. - ## What this plugin does When using the `class` keyword for defining classes (required by TypeScript), JSDoc requires `@classdesc` and `@extends` annotations. With this plugin, no `@classdesc` and `@extends` annotations are needed. @@ -121,6 +110,14 @@ This syntax is also used when referring to types of `@typedef`s and `@enum`s. */ ``` +## Module id resolution + +For resolving module ids, this plugin mirrors the method used by JSDoc: + +1. Parse the referenced module for an `@module` tag. +2. If a tag is found and it has an explicit id, use that. +3. If a tag is found, but it doesn't have an explicit id, use the module's file path relative to the nearest shared parent directory, and remove the file extension. + ## Contributing If you are interested in making a contribution to the project, please see the [contributing page](./contributing.md) for details on getting your development environment set up. diff --git a/index.js b/index.js index 29eddef..d7e7b44 100644 --- a/index.js +++ b/index.js @@ -4,20 +4,6 @@ const fs = require('fs'); const env = require('jsdoc/env'); // eslint-disable-line import/no-unresolved const addInherited = require('jsdoc/augment').addInherited; // eslint-disable-line import/no-unresolved -const config = env.conf; -const moduleRoot = config.typescript ? config.typescript.moduleRoot : undefined; -const moduleRootAbsolute = moduleRoot - ? path.join(process.cwd(), moduleRoot) - : undefined; - -if (moduleRootAbsolute && !fs.existsSync(moduleRootAbsolute)) { - throw new Error( - 'Directory "' + - moduleRootAbsolute + - '" does not exist. Check the "typescript.moduleRoot" config option for jsdoc-plugin-typescript', - ); -} - const importRegEx = /import\(["']([^"']*)["']\)(?:\.([^ \.\|\}><,\)=#\n]*))?([ \.\|\}><,\)=#\n])/g; const typedefRegEx = /@typedef \{[^\}]*\} (\S+)/g; @@ -25,13 +11,11 @@ const noClassdescRegEx = /@(typedef|module|type)/; const extensionReplaceRegEx = /\.m?js$/; const extensionEnsureRegEx = /(\.js)?$/; const slashRegEx = /\\/g; -const leadingPathSegmentRegEx = /^(.?.[/\\])+/; const moduleInfos = {}; const fileNodes = {}; const resolvedPathCache = new Set(); -// Without explicit module ids, JSDoc will use the nearest shared parent directory /** @type {string} */ let implicitModuleRoot; @@ -76,14 +60,6 @@ function getModuleId(modulePath) { return moduleInfos[modulePath].id; } - // Use moduleRoot if set - if (moduleRootAbsolute) { - return path - .relative(moduleRootAbsolute, modulePath) - .replace(extensionReplaceRegEx, '') - .replace(leadingPathSegmentRegEx, ''); - } - // Search for explicit module id if (fileNodes[modulePath]) { for (const comment of fileNodes[modulePath].comments) { diff --git a/test/template/config.json b/test/template/config.json index 14b4f3c..402840d 100644 --- a/test/template/config.json +++ b/test/template/config.json @@ -5,17 +5,10 @@ "destination": "test/dest/actual.json" }, "source": { - "include": [ - "test/src" - ] + "include": ["test/src"] }, "tags": { "allowUnknownTags": true }, - "plugins": [ - "index.js" - ], - "typescript": { - "moduleRoot": "test/src" - } + "plugins": ["index.js"] } From 22e5fbfa4b96e294f6963842c41e1f40f5786c7a Mon Sep 17 00:00:00 2001 From: Daniel Knights <59598622+Daniel-Knights@users.noreply.github.com> Date: Thu, 12 Sep 2024 07:35:55 +0100 Subject: [PATCH 14/14] Update expected test output --- test/dest/expected.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/dest/expected.json b/test/dest/expected.json index b616185..c46750a 100644 --- a/test/dest/expected.json +++ b/test/dest/expected.json @@ -144,7 +144,7 @@ "params": [] }, { - "comment": "/**\n * @param {number} number A number.\n * @return {module:sub/NumberStore~NumberStore} A number store.\n */", + "comment": "/**\n * @param {number} number A number.\n * @return {module:test/sub/NumberStore~NumberStore} A number store.\n */", "meta": { "range": [ 495, @@ -176,7 +176,7 @@ { "type": { "names": [ - "module:sub/NumberStore~NumberStore" + "module:test/sub/NumberStore~NumberStore" ] }, "description": "A number store." @@ -306,7 +306,7 @@ "undocumented": true }, { - "comment": "/**\n * @param {module:sub/NumberStore~Options} options The options.\n */", + "comment": "/**\n * @param {module:test/sub/NumberStore~Options} options The options.\n */", "meta": { "range": [ 231, @@ -330,7 +330,7 @@ { "type": { "names": [ - "module:sub/NumberStore~Options" + "module:test/sub/NumberStore~Options" ] }, "description": "The options.", @@ -372,7 +372,7 @@ { "type": { "names": [ - "module:sub/NumberStore~Options" + "module:test/sub/NumberStore~Options" ] }, "description": "The options.", @@ -465,4 +465,4 @@ "longname": "module:test/sub/NumberStore", "kind": "member" } -] +] \ No newline at end of file