1 |
|
2 | import builtins from 'builtin-modules'
|
3 |
|
4 | import detective from 'detective'
|
5 | import path from 'path'
|
6 | import semver from 'semver'
|
7 |
|
8 | import Parser from './Parser'
|
9 | import { Person, SoftwarePackage } from '@stencila/schema'
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | export default class JavascriptParser extends Parser {
|
15 |
|
16 | |
17 |
|
18 |
|
19 |
|
20 | async parse (): Promise<SoftwarePackage | null> {
|
21 | if (this.exists('package.json')) {
|
22 | let data = JSON.parse(this.read('package.json'))
|
23 | return this.createPackage(data)
|
24 | } else {
|
25 | const files = this.glob(['**/*.js'])
|
26 | if (files.length) {
|
27 | const data = {
|
28 | name: path.basename(this.folder),
|
29 | dependencies: {}
|
30 | }
|
31 | for (let file of files) {
|
32 | const code = this.read(file)
|
33 | const requires = detective(code)
|
34 | for (let require of requires) {
|
35 | if (!builtins.includes(require)) {
|
36 |
|
37 | data.dependencies[require] = 'latest'
|
38 | }
|
39 | }
|
40 | }
|
41 | return this.createPackage(data)
|
42 | } else {
|
43 | return null
|
44 | }
|
45 | }
|
46 | }
|
47 |
|
48 | |
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | private async createPackage (data: any): Promise<SoftwarePackage> {
|
62 |
|
63 |
|
64 | const pkg = new SoftwarePackage()
|
65 |
|
66 |
|
67 | pkg.name = data.name
|
68 |
|
69 |
|
70 | pkg.version = data.version
|
71 |
|
72 |
|
73 | pkg.runtimePlatform = 'Node.js'
|
74 |
|
75 | pkg.license = data.license
|
76 | pkg.description = data.description
|
77 |
|
78 | if (data.author) {
|
79 | if (typeof data.author === 'string') {
|
80 | pkg.authors = [Person.fromText(data.author)]
|
81 | } else {
|
82 | let authorStr = ''
|
83 | if (data.author.name) authorStr = data.author.name
|
84 | if (data.author.email) authorStr += ` <${data.author.email}>`
|
85 | if (data.author.url) authorStr += ` (${data.author.url})`
|
86 |
|
87 | pkg.authors = [Person.fromText(authorStr)]
|
88 | }
|
89 | }
|
90 |
|
91 | if (data.repository) {
|
92 | if (typeof data.repository === 'string') {
|
93 | if (data.repository.match(/github:/)) {
|
94 | pkg.codeRepository = data.repository.replace(/github:/, 'https://github.com') + '/'
|
95 | } else if (data.repository.match(/gitlab:/)) {
|
96 | pkg.codeRepository = data.repository.replace(/gitlab:/, 'https://gitlab.com') + '/'
|
97 | } else if (data.repository.match(/bitbucket:/)) {
|
98 | pkg.codeRepository = data.repository.replace(/bitbucket:/, 'https://bitbucket.com') + '/'
|
99 | } else if (data.repository.match(/^[^\/]*\/[^\/]*$/)) {
|
100 | pkg.codeRepository = data.repository.replace(/^([^\/]*)\/([^\/]*)$/, 'https://www.npmjs.com/package/$1/$2') + '/'
|
101 | } else {
|
102 | pkg.codeRepository = data.repository
|
103 | }
|
104 | } else {
|
105 | pkg.codeRepository = data.repository.url
|
106 | }
|
107 | }
|
108 |
|
109 |
|
110 | if (data.dependencies) {
|
111 | pkg.softwareRequirements = await Promise.all(
|
112 | Object.entries(data.dependencies).map(async ([name, versionRange]) => {
|
113 |
|
114 |
|
115 |
|
116 | let version = 'latest'
|
117 | if (versionRange !== 'latest' && versionRange !== '*') {
|
118 | const range = semver.validRange(versionRange as string)
|
119 | if (range) {
|
120 | const match = range.match(/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)/)
|
121 | if (match) version = match[0]
|
122 | }
|
123 | }
|
124 |
|
125 |
|
126 |
|
127 | if (name[0] === '@') {
|
128 | name = name.replace('/', '%2f')
|
129 | version = '*'
|
130 | }
|
131 |
|
132 |
|
133 | const data = await this.fetch(`https://registry.npmjs.org/${name}/${version}`, {
|
134 | json: true,
|
135 | headers: {
|
136 | 'Accept': 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
|
137 | }
|
138 | })
|
139 |
|
140 | if (data) {
|
141 | return this.createPackage(data)
|
142 | } else {
|
143 |
|
144 | const dependency = new SoftwarePackage()
|
145 | dependency.name = name
|
146 | dependency.version = versionRange as string
|
147 | dependency.runtimePlatform = 'Node.js'
|
148 | return dependency
|
149 | }
|
150 | })
|
151 | )
|
152 | }
|
153 |
|
154 | return pkg
|
155 | }
|
156 | }
|