-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathlinkspector.js
169 lines (145 loc) · 5.19 KB
/
linkspector.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import { execSync } from 'child_process'
import { readFileSync } from 'fs'
import path from 'path'
import yaml from 'js-yaml'
import dotenv from 'dotenv'
import { validateConfig } from './lib/validate-config.js'
import { prepareFilesList } from './lib/prepare-file-list.js'
import { extractMarkdownHyperlinks } from './lib/extract-markdown-hyperlinks.js'
import { extractAsciiDocLinks } from './lib/extract-asciidoc-links.js'
import { getUniqueLinks } from './lib/get-unique-links.js'
import { checkHyperlinks } from './lib/batch-check-links.js'
import { updateLinkStatusObj } from './lib/update-linkstatus-obj.js'
// Load environment variables from .env file
dotenv.config()
// Function to replace placeholders with environment variables
function replaceEnvVariables(config) {
const configString = JSON.stringify(config)
const replacedConfigString = configString.replace(
/\$\{(\w+)\}/g,
(_, name) => process.env[name] || ''
)
return JSON.parse(replacedConfigString)
}
// Function to check if git is installed
function isGitInstalled() {
try {
execSync('git --version', { stdio: 'ignore' })
return true
} catch (error) {
return false
}
}
export async function* linkspector(configFile, cmd) {
//Use default configuration if no config file is specified
let config = {}
let defaultConfig = {
dirs: ['.'],
useGitIgnore: true,
}
try {
let configContent = readFileSync(configFile, 'utf8')
// parse configFile
// Check if the YAML content is empty
if (!configContent.trim()) {
throw new Error('The configuration file is empty.')
}
// Parse the YAML content
config = yaml.load(configContent)
// Check if the parsed YAML object is null or lacks properties
if (config === null || Object.keys(config).length === 0) {
throw new Error('Failed to parse the YAML content.')
}
// Replace environment variables in the configuration
config = replaceEnvVariables(config)
try {
const isValid = await validateConfig(config)
if (!isValid) {
console.error('Validation failed!')
process.exit(1)
}
} catch (error) {
console.error(`💥 Error: Please check your configuration file.`)
process.exit(1)
}
} catch (err) {
if (err.code === 'ENOENT') {
if (!cmd.json) {
console.log(
'Configuration file not found. Using default configuration.'
)
}
config = defaultConfig
} else {
throw new Error(err)
}
}
// Prepare the list of files to check
let filesToCheck = prepareFilesList(config)
// Convert all paths in filesToCheck to relative paths
filesToCheck = filesToCheck.map((file) => path.relative(process.cwd(), file))
// Check if only modified files should be checked
if (config.modifiedFilesOnly) {
// Check if git is installed
if (!isGitInstalled()) {
console.error(
'Error: Git is not installed or not found in the system path.'
)
process.exit(1)
}
// Get the list of modified files from the last git commit
const modifiedFiles = execSync('git diff --name-only HEAD HEAD~1', {
encoding: 'utf8',
}).split('\n')
// Filter out files that are not in the list of files to check or do not have the correct extension
const modifiedFilesToCheck = modifiedFiles.filter((file) => {
const fileExtension = path.extname(file).substring(1).toLowerCase()
return (
filesToCheck.includes(file) &&
(config.fileExtensions || ['md']).includes(fileExtension)
)
})
// If no modified files are in the list of files to check, exit with a message
if (modifiedFilesToCheck.length === 0) {
if (cmd.json) {
console.log('{}')
} else {
console.log(
'No modified files to check, skipping checking. To enable checking all files set modifiedFilesOnly: false and rerun the check.'
)
}
process.exit(0)
}
// Otherwise, only check the modified files
filesToCheck = modifiedFilesToCheck
}
// Process each file
for (const file of filesToCheck) {
const relativeFilePath = path.relative(process.cwd(), file)
// Get the file extension
const fileExtension = path.extname(file).substring(1).toLowerCase() // Get the file extension without the leading dot and convert to lowercase
let astNodes
// Check the file extension and use the appropriate function to extract links
if (
['asciidoc', 'adoc', 'asc'].includes(fileExtension) &&
config.fileExtensions &&
config.fileExtensions.includes(fileExtension)
) {
astNodes = await extractAsciiDocLinks(file, config)
} else {
const fileContent = readFileSync(file, 'utf8')
astNodes = extractMarkdownHyperlinks(fileContent, config)
}
// Get unique hyperlinks
const uniqueLinks = getUniqueLinks(astNodes)
// Check the status of hyperlinks
const linkStatus = await checkHyperlinks(uniqueLinks, config, file)
// Update linkStatusObjects with information about removed links
const updatedLinkStatus = updateLinkStatusObj(astNodes, linkStatus)
// Yield an object with the relative file path and its result
yield {
file: relativeFilePath,
result: updatedLinkStatus,
}
}
}