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 = {
|
const plugin = {
|
||||||
meta: { name: 'open-pencil' },
|
meta: { name: 'open-pencil' },
|
||||||
rules: {
|
rules: {
|
||||||
'no-inline-named-types': noInlineNamedTypes,
|
'no-inline-named-types': noInlineNamedTypes,
|
||||||
'no-structuredclone-scene-arrays': noStructuredCloneSceneArrays,
|
'no-structuredclone-scene-arrays': noStructuredCloneSceneArrays,
|
||||||
'no-math-random': noMathRandom,
|
'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",
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||||
"plugins": ["typescript", "import", "unicorn", "vue"],
|
"plugins": ["typescript", "import", "unicorn", "vue"],
|
||||||
"jsPlugins": ["./lint/plugin.ts"],
|
"jsPlugins": ["./lint/plugin.js"],
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es2024": true
|
"es2024": true
|
||||||
|
|
@ -31,6 +31,9 @@
|
||||||
"typescript/no-unnecessary-condition": "error",
|
"typescript/no-unnecessary-condition": "error",
|
||||||
"typescript/no-unnecessary-type-assertion": "error",
|
"typescript/no-unnecessary-type-assertion": "error",
|
||||||
"typescript/no-unnecessary-type-parameters": "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-optional-chain": "error",
|
||||||
"typescript/prefer-for-of": "error",
|
"typescript/prefer-for-of": "error",
|
||||||
|
|
||||||
|
|
@ -42,6 +45,11 @@
|
||||||
"unicorn/no-instanceof-array": "error",
|
"unicorn/no-instanceof-array": "error",
|
||||||
"unicorn/no-typeof-undefined": "error",
|
"unicorn/no-typeof-undefined": "error",
|
||||||
"unicorn/no-nested-ternary": "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",
|
"unicorn/consistent-existence-index-check": "error",
|
||||||
|
|
||||||
"vue/no-arrow-functions-in-watch": "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"
|
"Matrix": "m00:number,m01:number,m02:number,m10:number,m11:number,m12:number"
|
||||||
}],
|
}],
|
||||||
"open-pencil/no-structuredclone-scene-arrays": "error",
|
"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": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ function expandRuns(runs: StyleRun[], textLength: number): (CharacterStyleOverri
|
||||||
const chars: (CharacterStyleOverride | null)[] = new Array(textLength).fill(null)
|
const chars: (CharacterStyleOverride | null)[] = new Array(textLength).fill(null)
|
||||||
for (const run of runs) {
|
for (const run of runs) {
|
||||||
for (let i = run.start; i < run.start + run.length && i < textLength; i++) {
|
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
|
return chars
|
||||||
|
|
@ -29,7 +29,7 @@ export function applyStyleToRange(
|
||||||
const chars = expandRuns(runs, textLength)
|
const chars = expandRuns(runs, textLength)
|
||||||
|
|
||||||
for (let i = start; i < end && i < textLength; i++) {
|
for (let i = start; i < end && i < textLength; i++) {
|
||||||
chars[i] = { ...(chars[i] ?? {}), ...patch }
|
chars[i] = { ...chars[i], ...patch }
|
||||||
}
|
}
|
||||||
|
|
||||||
return compactRuns(chars)
|
return compactRuns(chars)
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,10 @@ function serializeNodeProps(raw: SceneNode): string {
|
||||||
lines.push(`size: ${raw.width} ${raw.height}`)
|
lines.push(`size: ${raw.width} ${raw.height}`)
|
||||||
lines.push(`pos: ${raw.x} ${raw.y}`)
|
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)}`)
|
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) {
|
if (solidStroke) {
|
||||||
lines.push(`stroke: ${colorToHex(solidStroke.color)}`)
|
lines.push(`stroke: ${colorToHex(solidStroke.color)}`)
|
||||||
if (solidStroke.weight) lines.push(`strokeWeight: ${solidStroke.weight}`)
|
if (solidStroke.weight) lines.push(`strokeWeight: ${solidStroke.weight}`)
|
||||||
|
|
@ -155,12 +155,12 @@ export const analyzeColors = defineTool({
|
||||||
|
|
||||||
const boundVars = raw.boundVariables
|
const boundVars = raw.boundVariables
|
||||||
for (const fill of raw.fills) {
|
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)
|
trackColor(colorMap, fill.color, boundVars?.['fills'] ? String(boundVars['fills']) : null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const stroke of raw.strokes) {
|
for (const stroke of raw.strokes) {
|
||||||
if (stroke.visible !== false) {
|
if (stroke.visible) {
|
||||||
trackColor(colorMap, stroke.color, boundVars?.['strokes'] ? String(boundVars['strokes']) : null)
|
trackColor(colorMap, stroke.color, boundVars?.['strokes'] ? String(boundVars['strokes']) : null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue