1+ const fs = require ( 'fs' )
2+ const path = require ( 'path' )
3+ const findUp = require ( 'find-up' )
4+ const semver = require ( 'semver' )
5+
6+ const defaults = {
7+ optional : 'false' ,
8+ dev : 'false' ,
9+ peers : 'false' ,
10+ production : 'false' ,
11+ }
12+
13+ const npmPkgUrl = 'https://npmjs.org/package/'
14+
15+
16+ function findPkg ( dir ) {
17+ const pkgPath = findUp . sync ( 'package.json' , { cwd : dir } )
18+ if ( ! pkgPath ) throw new Error ( 'No package.json file found' )
19+ return pkgPath
20+ }
21+
22+ function sanitizeSemver ( version , maxLength = 10 , truncateStr = '...' ) {
23+ if ( semver . valid ( version ) ) return version
24+
25+ const adjustedLength = maxLength - truncateStr . length
26+
27+ return version . length > adjustedLength
28+ ? [ version . substr ( 0 , adjustedLength ) , truncateStr ] . join ( '' )
29+ : version
30+ }
31+
32+ function convertRepositoryToUrl ( repository , name ) {
33+ let repo = ( repository . url ? repository . url : repository ) . replace ( '.git' , '' )
34+
35+ if ( repo . startsWith ( 'http' ) ) {
36+ return repo
37+ } else if ( repo . startsWith ( 'git://' ) ) {
38+ return repo . replace ( 'git://' , 'https://' )
39+ } else if ( repo . startsWith ( 'git+ssh' ) ) {
40+ const [ full , url ] = repo . match ( / ^ g i t \+ s s h \: \/ \/ g i t \@ ( .* ) $ / )
41+ return [ `https://` , url ] . join ( '' )
42+ } else if ( repo . startsWith ( 'git@' ) ) {
43+ return repo . replace ( 'git@' , 'https://' ) . replace ( ':' , '/' )
44+ } else {
45+ return [ 'https://github.com/' , repo ] . join ( '' )
46+ }
47+
48+ return repo
49+ }
50+
51+ function getPkgUrl ( pkg ) {
52+ const { name, repository, homepage, bugs } = pkg
53+
54+ if ( homepage ) return homepage
55+ if ( repository ) return convertRepositoryToUrl ( repository , name )
56+ if ( bugs ) return bugs . url || bugs
57+ return `https://npmjs.org/package/${ name } `
58+ }
59+
60+ function sanitizeLicense ( license ) {
61+ return license ? license : 'UNLICENSED'
62+ }
63+
64+ const readDependencies = ( pkg ) => ( manifest , type ) => {
65+ const dependencyType = type || 'production'
66+ let dependencies
67+
68+ if ( type === 'production' ) {
69+ dependencies = pkg . dependencies
70+ } else {
71+ dependencies = pkg [ `${ type } Dependencies` ]
72+ }
73+
74+ return manifest . concat (
75+ Object . keys ( dependencies || { } ) . map ( ( name ) => {
76+ const localPkgPath = findUp . sync ( `node_modules/${ name } /package.json` )
77+ const localPkg = JSON . parse ( fs . readFileSync ( localPkgPath , 'utf8' ) )
78+ const { description, homepage, version, repository, license } = localPkg
79+
80+ return {
81+ name,
82+ semver : sanitizeSemver ( dependencies [ name ] ) ,
83+ version,
84+ description,
85+ url : getPkgUrl ( localPkg ) ,
86+ license : sanitizeLicense ( license ) ,
87+ dependencyType,
88+ }
89+ } )
90+ )
91+ }
92+
93+ function renderDependencies ( dependency ) {
94+ const { name, semver, version, license, description, url, dependencyType } =
95+ dependency
96+
97+ return [
98+ '' ,
99+ `[${ [ name , semver ] . join ( '@' ) } ](${ url } )` ,
100+ description ,
101+ version ,
102+ license ,
103+ dependencyType ,
104+ '' ,
105+ ] . join ( ' | ' )
106+ }
107+
108+ /**
109+ * ### > dependencyTable
110+ *
111+ * Generate a table of dependencies with links to their repositories, version information, and descriptions
112+ *
113+ * **Options:**
114+ * - `pkg` (optional): Relative path to package.json file. Default: auto-detected from current directory
115+ * - `production` (optional): Include production dependencies. Default `false`
116+ * - `dev` (optional): Include dev dependencies. Default `false`
117+ * - `peer` (optional): Include peer dependencies. Default `false`
118+ * - `optional` (optional): Include optional dependencies. Default `false`
119+ *
120+ * **Example:**
121+ * ```md
122+ * <!-- doc-gen dependencyTable production=true dev=true -->
123+ * Dependency table will be generated here
124+ * <!-- end-doc-gen -->
125+ * ```
126+ *
127+ * Default `matchWord` is `doc-gen`
128+ *
129+ * ---
130+ * @param {string } content The current content of the comment block
131+ * @param {object } options The options passed in from the comment declaration
132+ * @param {string } originalPath The path of the file being processed
133+ * @return {string } Dependency table in markdown format
134+ */
135+ function dependencyTable ( { content, options = { } , originalPath } ) {
136+ const opts = Object . assign ( { } , defaults , options )
137+
138+ let pkgPath
139+
140+ if ( opts . pkg ) {
141+ pkgPath = path . resolve ( path . dirname ( originalPath ) , opts . pkg )
142+ } else {
143+ pkgPath = findPkg ( originalPath )
144+ }
145+
146+ const pkg = JSON . parse ( fs . readFileSync ( pkgPath , 'utf8' ) )
147+
148+ const headers = [
149+ '| **Dependency** | **Description** | **Version** | **License** | **Type** |' ,
150+ '| -------------- | --------------- | ----------- | ----------- | -------- |' ,
151+ ]
152+
153+ const types = [ 'production' , 'peer' , 'optional' , 'dev' ]
154+
155+ const declaredTypes = types . filter ( ( type ) => opts [ type ] === 'true' )
156+
157+ const deps = ( declaredTypes . length ? declaredTypes : types )
158+ . concat ( [ '' ] )
159+ . reduce ( readDependencies ( pkg ) , [ ] )
160+ . map ( renderDependencies )
161+
162+ return headers . concat ( deps ) . join ( '\n' )
163+ }
164+
165+ module . exports = dependencyTable
0 commit comments