@ohos-rs/oxk 0.5.0 → 0.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/bin/format.js +4 -232
- package/bin/oxk.js +29 -0
- package/configuration_schema.json +4331 -0
- package/format.d.ts +14 -0
- package/format.js +24 -3
- package/index.d.ts +16 -0
- package/index.js +54 -52
- package/lint.js +9 -0
- package/oxlint-runtime/js_config.cjs +6 -6
- package/oxlint-runtime/plugins.cjs +8 -8
- package/package.json +27 -17
package/bin/format.js
CHANGED
|
@@ -1,236 +1,8 @@
|
|
|
1
|
-
const {
|
|
2
|
-
const path = require('node:path')
|
|
3
|
-
const { glob } = require('glob')
|
|
1
|
+
const { formatFiles: nativeFormatFiles } = require('../format.js')
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const RECOVERABLE_ERROR_PATTERNS = [
|
|
9
|
-
'Unsupported file type',
|
|
10
|
-
'requires external formatter support',
|
|
11
|
-
'External formatter is required',
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
function stripJsonComments(text) {
|
|
15
|
-
let result = ''
|
|
16
|
-
let inString = false
|
|
17
|
-
let stringQuote = ''
|
|
18
|
-
let isEscaped = false
|
|
19
|
-
let inLineComment = false
|
|
20
|
-
let inBlockComment = false
|
|
21
|
-
|
|
22
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
23
|
-
const char = text[index]
|
|
24
|
-
const next = text[index + 1]
|
|
25
|
-
|
|
26
|
-
if (inLineComment) {
|
|
27
|
-
if (char === '\n') {
|
|
28
|
-
inLineComment = false
|
|
29
|
-
result += char
|
|
30
|
-
}
|
|
31
|
-
continue
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (inBlockComment) {
|
|
35
|
-
if (char === '*' && next === '/') {
|
|
36
|
-
inBlockComment = false
|
|
37
|
-
index += 1
|
|
38
|
-
}
|
|
39
|
-
continue
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (inString) {
|
|
43
|
-
result += char
|
|
44
|
-
if (isEscaped) {
|
|
45
|
-
isEscaped = false
|
|
46
|
-
} else if (char === '\\') {
|
|
47
|
-
isEscaped = true
|
|
48
|
-
} else if (char === stringQuote) {
|
|
49
|
-
inString = false
|
|
50
|
-
stringQuote = ''
|
|
51
|
-
}
|
|
52
|
-
continue
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (char === '"' || char === "'") {
|
|
56
|
-
inString = true
|
|
57
|
-
stringQuote = char
|
|
58
|
-
result += char
|
|
59
|
-
continue
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (char === '/' && next === '/') {
|
|
63
|
-
inLineComment = true
|
|
64
|
-
index += 1
|
|
65
|
-
continue
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (char === '/' && next === '*') {
|
|
69
|
-
inBlockComment = true
|
|
70
|
-
index += 1
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
result += char
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return result
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function resolveConfigPath(cwd, explicitPath) {
|
|
81
|
-
if (explicitPath) {
|
|
82
|
-
const resolved = path.resolve(cwd, explicitPath)
|
|
83
|
-
if (!existsSync(resolved)) {
|
|
84
|
-
throw new Error(`Failed to read ${resolved}: File not found`)
|
|
85
|
-
}
|
|
86
|
-
return resolved
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
let current = cwd
|
|
90
|
-
for (;;) {
|
|
91
|
-
for (const filename of CONFIG_FILES) {
|
|
92
|
-
const candidate = path.join(current, filename)
|
|
93
|
-
if (existsSync(candidate)) {
|
|
94
|
-
return candidate
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const parent = path.dirname(current)
|
|
99
|
-
if (parent === current) {
|
|
100
|
-
return undefined
|
|
101
|
-
}
|
|
102
|
-
current = parent
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function loadConfig(cwd, explicitPath) {
|
|
107
|
-
const configPath = resolveConfigPath(cwd, explicitPath)
|
|
108
|
-
if (!configPath) {
|
|
109
|
-
return { config: {}, ignorePatterns: [] }
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const raw = readFileSync(configPath, 'utf8')
|
|
113
|
-
const parsed = JSON.parse(stripJsonComments(raw))
|
|
114
|
-
const ignorePatterns = Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns : []
|
|
115
|
-
return { config: parsed, ignorePatterns }
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function mergeConfig(baseConfig, cliOptions) {
|
|
119
|
-
return { ...baseConfig, ...cliOptions }
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function expandPatterns(patterns, cwd) {
|
|
123
|
-
const results = new Set()
|
|
124
|
-
|
|
125
|
-
for (const pattern of patterns) {
|
|
126
|
-
const absolutePattern = path.resolve(cwd, pattern)
|
|
127
|
-
const matches = await glob(absolutePattern, {
|
|
128
|
-
absolute: true,
|
|
129
|
-
dot: true,
|
|
130
|
-
nodir: true,
|
|
131
|
-
follow: false,
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
for (const match of matches) {
|
|
135
|
-
results.add(path.resolve(match))
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return results
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function isRecoverableError(errors) {
|
|
143
|
-
return errors.every((error) => RECOVERABLE_ERROR_PATTERNS.some((pattern) => error.includes(pattern)))
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function runWithConcurrency(items, limit, worker) {
|
|
147
|
-
let index = 0
|
|
148
|
-
const workers = Array.from({ length: Math.max(1, Math.min(limit, items.length || 1)) }, async () => {
|
|
149
|
-
for (;;) {
|
|
150
|
-
const current = index
|
|
151
|
-
index += 1
|
|
152
|
-
if (current >= items.length) {
|
|
153
|
-
return
|
|
154
|
-
}
|
|
155
|
-
await worker(items[current])
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
await Promise.all(workers)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function printErrors(filePath, errors, prefix) {
|
|
163
|
-
console.error(`${prefix} ${filePath}:`)
|
|
164
|
-
for (const error of errors) {
|
|
165
|
-
console.error(` ${error}`)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function formatFiles({ patterns, configPath, excludes, threadCount, cliOptions }) {
|
|
170
|
-
const cwd = process.cwd()
|
|
171
|
-
|
|
172
|
-
if (patterns.length === 0) {
|
|
173
|
-
throw new Error('Missing file pattern')
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const { config, ignorePatterns } = loadConfig(cwd, configPath)
|
|
177
|
-
const mergedConfig = mergeConfig(config, cliOptions)
|
|
178
|
-
const allExcludes = excludes.concat(ignorePatterns)
|
|
179
|
-
|
|
180
|
-
const files = await expandPatterns(patterns, cwd)
|
|
181
|
-
if (allExcludes.length > 0) {
|
|
182
|
-
const excludedFiles = await expandPatterns(allExcludes, cwd)
|
|
183
|
-
for (const excluded of excludedFiles) {
|
|
184
|
-
files.delete(excluded)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const sortedFiles = Array.from(files).sort()
|
|
189
|
-
if (sortedFiles.length === 0) {
|
|
190
|
-
throw new Error('No files matched the provided patterns (after excludes)')
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
let formattedCount = 0
|
|
194
|
-
let hasRecoverableErrors = false
|
|
195
|
-
let fatalError = null
|
|
196
|
-
|
|
197
|
-
await runWithConcurrency(sortedFiles, threadCount, async (filePath) => {
|
|
198
|
-
if (fatalError) {
|
|
199
|
-
return
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const sourceText = readFileSync(filePath, 'utf8')
|
|
203
|
-
if (sourceText.length === 0) {
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const displayPath = path.relative(cwd, filePath) || filePath
|
|
208
|
-
const result = await format(displayPath, sourceText, mergedConfig)
|
|
209
|
-
|
|
210
|
-
if (result.errors.length === 0) {
|
|
211
|
-
writeFileSync(filePath, result.code, 'utf8')
|
|
212
|
-
console.log(`Formatted: ${displayPath}`)
|
|
213
|
-
formattedCount += 1
|
|
214
|
-
return
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (isRecoverableError(result.errors)) {
|
|
218
|
-
printErrors(displayPath, result.errors, 'Warning formatting')
|
|
219
|
-
hasRecoverableErrors = true
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
fatalError = { filePath: displayPath, errors: result.errors }
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
if (fatalError) {
|
|
227
|
-
printErrors(fatalError.filePath, fatalError.errors, 'Error formatting')
|
|
228
|
-
process.exitCode = 1
|
|
229
|
-
return
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
console.log(`\nFormatted ${formattedCount} file(s)`)
|
|
233
|
-
if (hasRecoverableErrors) {
|
|
3
|
+
async function formatFiles(args) {
|
|
4
|
+
const success = await nativeFormatFiles(args)
|
|
5
|
+
if (!success) {
|
|
234
6
|
process.exitCode = 1
|
|
235
7
|
}
|
|
236
8
|
}
|
package/bin/oxk.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const VERSION = require('../package.json').version
|
|
4
4
|
const { formatFiles } = require('./format.js')
|
|
5
|
+
const { formatLsp } = require('../index.js')
|
|
5
6
|
const { lint } = require('../lint.js')
|
|
6
7
|
|
|
7
8
|
function parseBoolean(value, flagName) {
|
|
@@ -55,9 +56,12 @@ Lint Options:
|
|
|
55
56
|
Run "oxk lint --help" for the full lint option list.
|
|
56
57
|
|
|
57
58
|
Format Options:
|
|
59
|
+
--lsp
|
|
58
60
|
--config PATH
|
|
59
61
|
--thread, -t THREAD
|
|
60
62
|
--exclude PATTERN
|
|
63
|
+
--ignore-path PATH
|
|
64
|
+
--with-node-modules
|
|
61
65
|
--indent-style STYLE
|
|
62
66
|
--indent-width WIDTH
|
|
63
67
|
--line-ending ENDING
|
|
@@ -92,8 +96,11 @@ function parseFormatArgs(args) {
|
|
|
92
96
|
const formatArgs = {
|
|
93
97
|
patterns: [],
|
|
94
98
|
excludes: [],
|
|
99
|
+
ignorePaths: [],
|
|
100
|
+
withNodeModules: false,
|
|
95
101
|
threadCount: 1,
|
|
96
102
|
configPath: undefined,
|
|
103
|
+
lsp: false,
|
|
97
104
|
cliOptions: {},
|
|
98
105
|
}
|
|
99
106
|
|
|
@@ -110,6 +117,10 @@ function parseFormatArgs(args) {
|
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
switch (token.split('=')[0]) {
|
|
120
|
+
case '--lsp': {
|
|
121
|
+
formatArgs.lsp = true
|
|
122
|
+
break
|
|
123
|
+
}
|
|
113
124
|
case '--config': {
|
|
114
125
|
const { value, nextIndex } = parseOptionValue(args, index, token)
|
|
115
126
|
formatArgs.configPath = value
|
|
@@ -129,6 +140,16 @@ function parseFormatArgs(args) {
|
|
|
129
140
|
index = nextIndex
|
|
130
141
|
break
|
|
131
142
|
}
|
|
143
|
+
case '--ignore-path': {
|
|
144
|
+
const { value, nextIndex } = parseOptionValue(args, index, token)
|
|
145
|
+
formatArgs.ignorePaths.push(value)
|
|
146
|
+
index = nextIndex
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
case '--with-node-modules': {
|
|
150
|
+
formatArgs.withNodeModules = true
|
|
151
|
+
break
|
|
152
|
+
}
|
|
132
153
|
case '--indent-style': {
|
|
133
154
|
const { value, nextIndex } = parseOptionValue(args, index, token)
|
|
134
155
|
if (value !== 'tab' && value !== 'space') {
|
|
@@ -298,6 +319,14 @@ async function main() {
|
|
|
298
319
|
process.exit(0)
|
|
299
320
|
}
|
|
300
321
|
|
|
322
|
+
if (formatArgs.lsp) {
|
|
323
|
+
if (typeof formatLsp !== 'function') {
|
|
324
|
+
throw new Error('oxk format --lsp requires the native oxk binding')
|
|
325
|
+
}
|
|
326
|
+
await formatLsp()
|
|
327
|
+
return
|
|
328
|
+
}
|
|
329
|
+
|
|
301
330
|
await formatFiles(formatArgs)
|
|
302
331
|
} catch (error) {
|
|
303
332
|
console.error(error instanceof Error ? error.message : String(error))
|