Revert lint plugin to .js (oxlint uses Node.js internally), add new rules
Oxlint's JS plugin loader uses Node.js, not Bun — .ts fails in CI
with ERR_UNKNOWN_FILE_EXTENSION.
New rules:
- no-hand-rolled-color: flags rgba()/rgb() in template literals outside
color.ts — use colorToCSS()/colorToHex() helpers instead
- typescript/no-unnecessary-boolean-literal-compare
- typescript/no-unnecessary-template-expression
- typescript/no-unnecessary-type-arguments
- unicorn/no-useless-fallback-in-spread
- unicorn/no-length-as-slice-end
- unicorn/no-unnecessary-await
- unicorn/prefer-string-starts-ends-with
Fixed violations: visible !== false → fill.visible in analyze.ts,
removed ?? {} fallbacks in style-runs.ts spread.
This commit is contained in:
parent
df0ccbca48
commit
2f06c65c7c
|
|
@ -121,12 +121,39 @@ const noMathRandom = {
|
|||
},
|
||||
}
|
||||
|
||||
const noHandRolledColor = {
|
||||
meta: {
|
||||
docs: {
|
||||
description:
|
||||
'Disallow hand-rolled color conversions — use helpers from color.ts (colorToCSS, colorToHex, parseColor, etc.)',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
const file = context.filename ?? context.getFilename?.()
|
||||
if (file?.endsWith('color.ts') || file?.endsWith('color.js')) return {}
|
||||
|
||||
return {
|
||||
TemplateLiteral(node) {
|
||||
const raw = context.sourceCode.getText(node)
|
||||
if (/rgba?\s*\(/.test(raw)) {
|
||||
context.report({
|
||||
node,
|
||||
message: "Use colorToCSS() or colorToHex() from color.ts instead of hand-rolled rgba()/rgb() strings.",
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const plugin = {
|
||||
meta: { name: 'open-pencil' },
|
||||
rules: {
|
||||
'no-inline-named-types': noInlineNamedTypes,
|
||||
'no-structuredclone-scene-arrays': noStructuredCloneSceneArrays,
|
||||
'no-math-random': noMathRandom,
|
||||
'no-hand-rolled-color': noHandRolledColor,
|
||||
},
|
||||
}
|
||||
|
||||
13
oxlint.json
13
oxlint.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": ["typescript", "import", "unicorn", "vue"],
|
||||
"jsPlugins": ["./lint/plugin.ts"],
|
||||
"jsPlugins": ["./lint/plugin.js"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2024": true
|
||||
|
|
@ -31,6 +31,9 @@
|
|||
"typescript/no-unnecessary-condition": "error",
|
||||
"typescript/no-unnecessary-type-assertion": "error",
|
||||
"typescript/no-unnecessary-type-parameters": "error",
|
||||
"typescript/no-unnecessary-boolean-literal-compare": "error",
|
||||
"typescript/no-unnecessary-template-expression": "error",
|
||||
"typescript/no-unnecessary-type-arguments": "error",
|
||||
"typescript/prefer-optional-chain": "error",
|
||||
"typescript/prefer-for-of": "error",
|
||||
|
||||
|
|
@ -42,6 +45,11 @@
|
|||
"unicorn/no-instanceof-array": "error",
|
||||
"unicorn/no-typeof-undefined": "error",
|
||||
"unicorn/no-nested-ternary": "error",
|
||||
|
||||
"unicorn/no-useless-fallback-in-spread": "error",
|
||||
"unicorn/no-length-as-slice-end": "error",
|
||||
"unicorn/no-unnecessary-await": "error",
|
||||
"unicorn/prefer-string-starts-ends-with": "error",
|
||||
"unicorn/consistent-existence-index-check": "error",
|
||||
|
||||
"vue/no-arrow-functions-in-watch": "error",
|
||||
|
|
@ -66,7 +74,8 @@
|
|||
"Matrix": "m00:number,m01:number,m02:number,m10:number,m11:number,m12:number"
|
||||
}],
|
||||
"open-pencil/no-structuredclone-scene-arrays": "error",
|
||||
"open-pencil/no-math-random": "error"
|
||||
"open-pencil/no-math-random": "error",
|
||||
"open-pencil/no-hand-rolled-color": "error"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function expandRuns(runs: StyleRun[], textLength: number): (CharacterStyleOverri
|
|||
const chars: (CharacterStyleOverride | null)[] = new Array(textLength).fill(null)
|
||||
for (const run of runs) {
|
||||
for (let i = run.start; i < run.start + run.length && i < textLength; i++) {
|
||||
chars[i] = { ...(chars[i] ?? {}), ...run.style }
|
||||
chars[i] = { ...chars[i], ...run.style }
|
||||
}
|
||||
}
|
||||
return chars
|
||||
|
|
@ -29,7 +29,7 @@ export function applyStyleToRange(
|
|||
const chars = expandRuns(runs, textLength)
|
||||
|
||||
for (let i = start; i < end && i < textLength; i++) {
|
||||
chars[i] = { ...(chars[i] ?? {}), ...patch }
|
||||
chars[i] = { ...chars[i], ...patch }
|
||||
}
|
||||
|
||||
return compactRuns(chars)
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ function serializeNodeProps(raw: SceneNode): string {
|
|||
lines.push(`size: ${raw.width} ${raw.height}`)
|
||||
lines.push(`pos: ${raw.x} ${raw.y}`)
|
||||
|
||||
const solidFill = raw.fills.find((f) => f.type === 'SOLID' && f.visible !== false)
|
||||
const solidFill = raw.fills.find((f) => f.type === 'SOLID' && f.visible)
|
||||
if (solidFill) lines.push(`fill: ${colorToHex(solidFill.color)}`)
|
||||
|
||||
const solidStroke = raw.strokes.find((s) => s.visible !== false)
|
||||
const solidStroke = raw.strokes.find((s) => s.visible)
|
||||
if (solidStroke) {
|
||||
lines.push(`stroke: ${colorToHex(solidStroke.color)}`)
|
||||
if (solidStroke.weight) lines.push(`strokeWeight: ${solidStroke.weight}`)
|
||||
|
|
@ -155,12 +155,12 @@ export const analyzeColors = defineTool({
|
|||
|
||||
const boundVars = raw.boundVariables
|
||||
for (const fill of raw.fills) {
|
||||
if (fill.type === 'SOLID' && fill.visible !== false) {
|
||||
if (fill.type === 'SOLID' && fill.visible) {
|
||||
trackColor(colorMap, fill.color, boundVars?.['fills'] ? String(boundVars['fills']) : null)
|
||||
}
|
||||
}
|
||||
for (const stroke of raw.strokes) {
|
||||
if (stroke.visible !== false) {
|
||||
if (stroke.visible) {
|
||||
trackColor(colorMap, stroke.color, boundVars?.['strokes'] ? String(boundVars['strokes']) : null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue