@swiftuijs/twill-linter 0.1.2 → 0.2.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/README.md CHANGED
@@ -41,3 +41,9 @@ Custom rule overrides should also match the virtual TS/TSX files, as with other
41
41
  Framework ESLint plugins can attach their ordinary TS/TSX rules to these virtual files. Keep ESLint formatting rules disabled and let Prettier handle layout; rewrites spanning generated syntax cannot always be mapped back safely.
42
42
 
43
43
  [Documentation](https://twill.evecalm.com/tooling) · [Issues](https://github.com/swiftuijs/twill/issues) · MIT licensed · Built by [forth.ink](https://forth.ink).
44
+
45
+ ## Added in 0.2.0
46
+
47
+ Twill 0.2.0 adds `if const value = lookup() { use(value); }` with branch-local nullish bindings and native destructuring. Group trailing calls in initializers, following Swift’s condition boundary. It emits native branches without a helper, closure or optional wrapper. See [syntax](https://twill.evecalm.com/syntax#branch-nullish-bindings) and [RFC 0018](https://github.com/swiftuijs/twill/blob/main/docs/rfcs/0018-optional-branch-bindings.md).
48
+
49
+ Twill 0.2.0 includes associated-value enums and `match` expressions: `match (state) { case State.loaded({ value }): value; default: 0; }`. Named payload bindings and erased factory descriptors compile to native switches without calling factories. Native switch statements retain JS fallthrough; match and switch expressions return one result without fallthrough. Use the coordinated 0.2.0 packages and extension. Compiler, formatter, linter, highlighter, export and editor changes are validated together. See [syntax and compatibility](https://twill.evecalm.com/syntax#match-expressions) and [RFC 0016](https://github.com/swiftuijs/twill/blob/main/docs/rfcs/0016-pattern-matching.md).
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { dirname } from "node:path";
9
9
  import { statSync } from "node:fs";
10
10
  var package_default = {
11
11
  name: "@swiftuijs/twill-linter",
12
- version: "0.1.2",
12
+ version: "0.2.0",
13
13
  type: "module",
14
14
  license: "MIT",
15
15
  repository: {
@@ -215,14 +215,16 @@ var processor = {
215
215
  const start = generated.getPositionOfLineAndCharacter(message.line - 1, message.column - 1);
216
216
  let offset = originalOffset(state, start);
217
217
  let expressionSwitch = false;
218
+ let expressionKeywordLength = 6;
218
219
  if (message.ruleId === "@typescript-eslint/switch-exhaustiveness-check" && state.project && (offset === void 0 || state.result.code[start] !== state.source[offset])) {
219
220
  const file = state.project.service.getProgram()?.getSourceFile(virtualFilename(filename));
220
221
  const visit = (node) => {
221
222
  if (ts.isSwitchStatement(node) && node.expression.getStart() === start) {
222
223
  const sourceStart = originalOffset(state, node.getStart());
223
- if (sourceStart !== void 0 && state.source.slice(sourceStart, sourceStart + 6) === "switch") {
224
+ if (sourceStart !== void 0 && /^(?:switch|match)\b/.test(state.source.slice(sourceStart))) {
224
225
  offset = sourceStart;
225
226
  expressionSwitch = true;
227
+ expressionKeywordLength = state.source.startsWith("match", sourceStart) ? 5 : 6;
226
228
  }
227
229
  } else if (start >= node.getFullStart() && start < node.end) ts.forEachChild(node, visit);
228
230
  };
@@ -230,7 +232,7 @@ var processor = {
230
232
  }
231
233
  if (offset === void 0 || !expressionSwitch && start < state.result.code.length && state.result.code[start] !== state.source[offset]) return [];
232
234
  const position = state.original.getLineAndCharacterOfPosition(offset);
233
- const endOffset = expressionSwitch ? offset + 6 : message.endLine && message.endColumn ? originalOffset(state, generated.getPositionOfLineAndCharacter(message.endLine - 1, message.endColumn - 1)) : void 0;
235
+ const endOffset = expressionSwitch ? offset + expressionKeywordLength : message.endLine && message.endColumn ? originalOffset(state, generated.getPositionOfLineAndCharacter(message.endLine - 1, message.endColumn - 1)) : void 0;
234
236
  const end = endOffset === void 0 ? void 0 : state.original.getLineAndCharacterOfPosition(endOffset);
235
237
  return [{
236
238
  ...message,
@@ -238,8 +240,8 @@ var processor = {
238
240
  column: position.character + 1,
239
241
  endLine: end && end.line + 1,
240
242
  endColumn: end && end.character + 1,
241
- fix: fix(state, message.fix),
242
- suggestions: message.suggestions?.flatMap((suggestion) => {
243
+ fix: expressionSwitch ? void 0 : fix(state, message.fix),
244
+ suggestions: expressionSwitch ? [] : message.suggestions?.flatMap((suggestion) => {
243
245
  const mapped = fix(state, suggestion.fix);
244
246
  return mapped ? [{
245
247
  ...suggestion,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["files","metadata"],"sources":["../package.json","../src/positions.ts","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@swiftuijs/twill-linter\",\n \"version\": \"0.1.2\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/swiftuijs/twill.git\",\n \"directory\": \"packages/linter\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n }\n },\n \"scripts\": {\n \"build\": \"vite build\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\",\n \"test:unit\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"test:package\": \"node tests/consumer.mjs\",\n \"prepack\": \"pnpm run build\",\n \"test\": \"pnpm run test:unit\"\n },\n \"dependencies\": {\n \"@swiftuijs/twill\": \"workspace:*\",\n \"@typescript-eslint/parser\": \"^8.71.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.71.0\",\n \"@eslint/js\": \"catalog:\",\n \"typescript\": \"catalog:\"\n },\n \"devDependencies\": {\n \"vite\": \"catalog:\",\n \"vite-plugin-dts\": \"^5.1.2\",\n \"@types/node\": \"catalog:\",\n \"typescript\": \"catalog:\",\n \"eslint\": \"catalog:\",\n \"vitest\": \"catalog:\",\n \"@vitest/coverage-v8\": \"catalog:\"\n },\n \"engines\": {\n \"node\": \"^20.19.0 || >=22.12.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"peerDependencies\": {\n \"eslint\": \"^9.0.0 || ^10.0.0\"\n },\n \"author\": {\n \"name\": \"forth.ink\",\n \"url\": \"https://forth.ink\"\n },\n \"homepage\": \"https://twill.evecalm.com/\",\n \"bugs\": {\n \"url\": \"https://github.com/swiftuijs/twill/issues\"\n },\n \"keywords\": [\n \"twill\",\n \"eslint\",\n \"eslint-plugin\",\n \"typescript\",\n \"linter\"\n ],\n \"description\": \"ESLint flat configurations for Twill with TypeScript rules, original-source diagnostics and safe fixes.\"\n}\n","/** Source-map lines use LF; ESLint positions also count CR and Unicode separators. */\nexport class SourcePositions {\n private native = [0];\n private mapped = [0];\n\n constructor(text: string) {\n const breaks = /\\r\\n|[\\n\\r\\u2028\\u2029]/g;\n while (breaks.exec(text)) this.native.push(breaks.lastIndex);\n for (let index = 0; index < text.length; index++)\n if (text[index] === '\\n') this.mapped.push(index + 1);\n }\n\n private position(starts: number[], offset: number) {\n let low = 0;\n let high = starts.length;\n while (low + 1 < high) {\n const middle = (low + high) >>> 1;\n if (starts[middle]! <= offset) low = middle;\n else high = middle;\n }\n return { line: low, character: offset - starts[low]! };\n }\n\n getLineAndCharacterOfPosition(offset: number) {\n return this.position(this.native, offset);\n }\n\n getPositionOfLineAndCharacter(line: number, column: number) {\n return this.native[line]! + column;\n }\n\n mapPosition(offset: number) {\n const position = this.position(this.mapped, offset);\n return { line: position.line + 1, column: position.character };\n }\n\n mapOffset(line: number, column: number) {\n return this.mapped[line - 1]! + column;\n }\n}\n","import {\n transform,\n originalPosition,\n generatedPosition,\n type TransformResult,\n} from '@swiftuijs/twill';\nimport { TwillProject, virtualFilename, sourceFilename } from '@swiftuijs/twill/project';\nimport * as parser from '@typescript-eslint/parser';\nimport typescriptPlugin from '@typescript-eslint/eslint-plugin';\nimport js from '@eslint/js';\nimport ts from 'typescript';\nimport { loadConfig } from '@swiftuijs/twill/config';\nimport { dirname, resolve } from 'node:path';\nimport { statSync } from 'node:fs';\nimport type { ESLint, Linter, Rule } from 'eslint';\nimport metadata from '../package.json' with { type: 'json' };\nimport { SourcePositions } from './positions.js';\n\ntype State = {\n source: string;\n filename: string;\n result?: TransformResult;\n error?: any;\n original: SourcePositions;\n generated?: SourcePositions;\n project?: TwillProject;\n};\nconst pending = new Map<string, State>();\nconst projects = new Map<string, { project: TwillProject; times: Map<string, number> }>();\nconst normalize = (filename: string) => filename.replaceAll('\\\\', '/');\nconst stamp = (filename: string) => {\n try {\n return statSync(filename).mtimeMs;\n } catch {\n return -1;\n }\n};\n\nfunction projectFor(filename: string) {\n const config = ts.findConfigFile(dirname(filename), ts.sys.fileExists);\n if (!config) throw new Error('Typed Twill linting requires a tsconfig.json.');\n let cached = projects.get(config);\n if (\n cached &&\n cached.project.configFiles.some((file) => cached!.times.get(file) !== stamp(file))\n ) {\n cached.project.dispose();\n projects.delete(config);\n cached = undefined;\n }\n if (!cached) {\n cached = { project: new TwillProject(config), times: new Map() };\n for (const file of cached.project.configFiles) cached.times.set(file, stamp(file));\n if (projects.size >= 8) {\n const oldest = projects.keys().next().value!;\n projects.get(oldest)!.project.dispose();\n projects.delete(oldest);\n }\n projects.set(config, cached);\n }\n for (const file of cached.project.sourceFiles()) {\n const time = stamp(file);\n if (cached.times.has(file) && cached.times.get(file) !== time) cached.project.refresh(file);\n cached.times.set(file, time);\n }\n return cached.project;\n}\n\nfunction originalOffset(state: State, offset: number) {\n const position = state.generated!.mapPosition(offset);\n const point = originalPosition(state.result!, position.line, position.column);\n return point.line == null || point.column == null\n ? undefined\n : state.original.mapOffset(point.line, point.column);\n}\nfunction fix(state: State, value: Rule.Fix | undefined): Rule.Fix | undefined {\n if (!value) return undefined;\n const [start, end] = value.range;\n const offset = originalOffset(state, start);\n if (offset === undefined) return undefined;\n const position = state.original.mapPosition(offset);\n const generated = generatedPosition(state.result!, position.line, position.column);\n if (\n generated.line == null ||\n generated.column == null ||\n state.generated!.mapOffset(generated.line, generated.column) !== start ||\n state.source.slice(offset, offset + end - start) !== state.result!.code.slice(start, end)\n )\n return undefined;\n return { range: [offset, offset + end - start], text: value.text };\n}\n\nconst processor: Linter.Processor = {\n supportsAutofix: true,\n preprocess(source, filename) {\n filename = normalize(filename);\n const state: State = {\n source,\n filename,\n original: new SourcePositions(source),\n };\n pending.set(filename, state);\n try {\n // A project supplies the same inherited JSX settings as builds/checking.\n const config = ts.findConfigFile(dirname(filename), ts.sys.fileExists);\n state.result = transform(source, {\n ...loadConfig(config ? dirname(config) : dirname(filename)),\n filename,\n });\n state.generated = new SourcePositions(state.result.code);\n return [\n {\n text: state.result.code,\n filename: filename.endsWith('.twillx') ? 'source.tsx' : 'source.ts',\n },\n ];\n } catch (error) {\n state.error = error;\n return [];\n }\n },\n postprocess(lists, filename) {\n filename = normalize(filename);\n const state = pending.get(filename)!;\n pending.delete(filename);\n state.project?.update(filename);\n if (state.error)\n return [\n {\n ruleId: null,\n severity: 2,\n fatal: true,\n message: state.error.message,\n line: state.error.line ?? 1,\n column: (state.error.column ?? 0) + 1,\n },\n ];\n return lists.flat().flatMap((message) => {\n if (!message.line || !message.column) return [message];\n const generated = state.generated!;\n const start = generated.getPositionOfLineAndCharacter(message.line - 1, message.column - 1);\n let offset = originalOffset(state, start);\n let expressionSwitch = false;\n if (\n message.ruleId === '@typescript-eslint/switch-exhaustiveness-check' &&\n state.project &&\n (offset === undefined || state.result!.code[start] !== state.source[offset])\n ) {\n // Expression subjects use a generated once-evaluated local. Report the\n // missing union arm on the source-backed switch keyword instead.\n const file = state.project.service.getProgram()?.getSourceFile(virtualFilename(filename));\n const visit = (node: ts.Node) => {\n if (ts.isSwitchStatement(node) && node.expression.getStart() === start) {\n const sourceStart = originalOffset(state, node.getStart());\n if (\n sourceStart !== undefined &&\n state.source.slice(sourceStart, sourceStart + 6) === 'switch'\n ) {\n offset = sourceStart;\n expressionSwitch = true;\n }\n } else if (start >= node.getFullStart() && start < node.end) ts.forEachChild(node, visit);\n };\n if (file) visit(file);\n }\n if (\n offset === undefined ||\n (!expressionSwitch &&\n start < state.result!.code.length &&\n state.result!.code[start] !== state.source[offset])\n )\n return [];\n const position = state.original.getLineAndCharacterOfPosition(offset);\n const endOffset = expressionSwitch\n ? offset + 6\n : message.endLine && message.endColumn\n ? originalOffset(\n state,\n generated.getPositionOfLineAndCharacter(message.endLine - 1, message.endColumn - 1),\n )\n : undefined;\n const end =\n endOffset === undefined\n ? undefined\n : state.original.getLineAndCharacterOfPosition(endOffset);\n return [\n {\n ...message,\n line: position.line + 1,\n column: position.character + 1,\n endLine: end && end.line + 1,\n endColumn: end && end.character + 1,\n fix: fix(state, message.fix),\n suggestions: message.suggestions?.flatMap((suggestion) => {\n const mapped = fix(state, suggestion.fix);\n return mapped ? [{ ...suggestion, fix: mapped }] : [];\n }),\n },\n ];\n });\n },\n};\n\nconst virtualParser = {\n meta: { name: '@swiftuijs/twill-linter/parser', version: metadata.version },\n parseForESLint(code: string, options: any) {\n const original = normalize(options.filePath ?? '').replace(/\\/(?:\\d+_)?source\\.tsx?$/, '');\n const state = pending.get(original);\n if (!state) return parser.parseForESLint(code, options);\n const typed = options.projectService || options.project || options.programs;\n if (!typed) return parser.parseForESLint(code, options);\n const project = projectFor(original);\n project.update(original, state.source);\n state.project = project;\n return parser.parseForESLint(code, {\n ...options,\n filePath: virtualFilename(original),\n projectService: false,\n project: undefined,\n programs: [project.service.getProgram()!],\n });\n },\n};\n\nconst plugin: ESLint.Plugin & { configs: Record<string, Linter.Config[]> } = {\n meta: { name: '@swiftuijs/twill-linter', version: metadata.version },\n processors: { twill: processor },\n configs: {} as Record<string, Linter.Config[]>,\n};\nconst files = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'];\nfunction configuration(typed: boolean): Linter.Config[] {\n const recommended = typescriptPlugin.configs[\n typed ? 'flat/recommended-type-checked' : 'flat/recommended'\n ] as Linter.Config[];\n return [\n js.configs.recommended,\n { files: ['**/*.twill', '**/*.twillx'], plugins: { twill: plugin }, processor: 'twill/twill' },\n ...recommended.map((config) => ({\n ...config,\n files,\n languageOptions: {\n ...config.languageOptions,\n parser: virtualParser,\n ...(typed ? { parserOptions: { projectService: true } } : {}),\n },\n })),\n ...(typed\n ? [\n {\n files,\n rules: {\n '@typescript-eslint/switch-exhaustiveness-check': [\n 'error',\n { considerDefaultExhaustiveForUnions: false },\n ] satisfies Linter.RuleEntry,\n },\n },\n ]\n : []),\n ];\n}\nplugin.configs.recommended = configuration(false);\nplugin.configs.recommendedTypeChecked = configuration(true);\nexport const configs: Record<string, Linter.Config[]> = plugin.configs;\nexport const processors: Record<string, Linter.Processor> = { twill: processor };\nexport function disposeProjects() {\n for (const { project } of projects.values()) project.dispose();\n projects.clear();\n pending.clear();\n}\nexport default plugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["files","metadata"],"sources":["../package.json","../src/positions.ts","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@swiftuijs/twill-linter\",\n \"version\": \"0.2.0\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/swiftuijs/twill.git\",\n \"directory\": \"packages/linter\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n }\n },\n \"scripts\": {\n \"build\": \"vite build\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\",\n \"test:unit\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"test:package\": \"node tests/consumer.mjs\",\n \"prepack\": \"pnpm run build\",\n \"test\": \"pnpm run test:unit\"\n },\n \"dependencies\": {\n \"@swiftuijs/twill\": \"workspace:*\",\n \"@typescript-eslint/parser\": \"^8.71.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.71.0\",\n \"@eslint/js\": \"catalog:\",\n \"typescript\": \"catalog:\"\n },\n \"devDependencies\": {\n \"vite\": \"catalog:\",\n \"vite-plugin-dts\": \"^5.1.2\",\n \"@types/node\": \"catalog:\",\n \"typescript\": \"catalog:\",\n \"eslint\": \"catalog:\",\n \"vitest\": \"catalog:\",\n \"@vitest/coverage-v8\": \"catalog:\"\n },\n \"engines\": {\n \"node\": \"^20.19.0 || >=22.12.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"peerDependencies\": {\n \"eslint\": \"^9.0.0 || ^10.0.0\"\n },\n \"author\": {\n \"name\": \"forth.ink\",\n \"url\": \"https://forth.ink\"\n },\n \"homepage\": \"https://twill.evecalm.com/\",\n \"bugs\": {\n \"url\": \"https://github.com/swiftuijs/twill/issues\"\n },\n \"keywords\": [\n \"twill\",\n \"eslint\",\n \"eslint-plugin\",\n \"typescript\",\n \"linter\"\n ],\n \"description\": \"ESLint flat configurations for Twill with TypeScript rules, original-source diagnostics and safe fixes.\"\n}\n","/** Source-map lines use LF; ESLint positions also count CR and Unicode separators. */\nexport class SourcePositions {\n private native = [0];\n private mapped = [0];\n\n constructor(text: string) {\n const breaks = /\\r\\n|[\\n\\r\\u2028\\u2029]/g;\n while (breaks.exec(text)) this.native.push(breaks.lastIndex);\n for (let index = 0; index < text.length; index++)\n if (text[index] === '\\n') this.mapped.push(index + 1);\n }\n\n private position(starts: number[], offset: number) {\n let low = 0;\n let high = starts.length;\n while (low + 1 < high) {\n const middle = (low + high) >>> 1;\n if (starts[middle]! <= offset) low = middle;\n else high = middle;\n }\n return { line: low, character: offset - starts[low]! };\n }\n\n getLineAndCharacterOfPosition(offset: number) {\n return this.position(this.native, offset);\n }\n\n getPositionOfLineAndCharacter(line: number, column: number) {\n return this.native[line]! + column;\n }\n\n mapPosition(offset: number) {\n const position = this.position(this.mapped, offset);\n return { line: position.line + 1, column: position.character };\n }\n\n mapOffset(line: number, column: number) {\n return this.mapped[line - 1]! + column;\n }\n}\n","import {\n transform,\n originalPosition,\n generatedPosition,\n type TransformResult,\n} from '@swiftuijs/twill';\nimport { TwillProject, virtualFilename, sourceFilename } from '@swiftuijs/twill/project';\nimport * as parser from '@typescript-eslint/parser';\nimport typescriptPlugin from '@typescript-eslint/eslint-plugin';\nimport js from '@eslint/js';\nimport ts from 'typescript';\nimport { loadConfig } from '@swiftuijs/twill/config';\nimport { dirname, resolve } from 'node:path';\nimport { statSync } from 'node:fs';\nimport type { ESLint, Linter, Rule } from 'eslint';\nimport metadata from '../package.json' with { type: 'json' };\nimport { SourcePositions } from './positions.js';\n\ntype State = {\n source: string;\n filename: string;\n result?: TransformResult;\n error?: any;\n original: SourcePositions;\n generated?: SourcePositions;\n project?: TwillProject;\n};\nconst pending = new Map<string, State>();\nconst projects = new Map<string, { project: TwillProject; times: Map<string, number> }>();\nconst normalize = (filename: string) => filename.replaceAll('\\\\', '/');\nconst stamp = (filename: string) => {\n try {\n return statSync(filename).mtimeMs;\n } catch {\n return -1;\n }\n};\n\nfunction projectFor(filename: string) {\n const config = ts.findConfigFile(dirname(filename), ts.sys.fileExists);\n if (!config) throw new Error('Typed Twill linting requires a tsconfig.json.');\n let cached = projects.get(config);\n if (\n cached &&\n cached.project.configFiles.some((file) => cached!.times.get(file) !== stamp(file))\n ) {\n cached.project.dispose();\n projects.delete(config);\n cached = undefined;\n }\n if (!cached) {\n cached = { project: new TwillProject(config), times: new Map() };\n for (const file of cached.project.configFiles) cached.times.set(file, stamp(file));\n if (projects.size >= 8) {\n const oldest = projects.keys().next().value!;\n projects.get(oldest)!.project.dispose();\n projects.delete(oldest);\n }\n projects.set(config, cached);\n }\n for (const file of cached.project.sourceFiles()) {\n const time = stamp(file);\n if (cached.times.has(file) && cached.times.get(file) !== time) cached.project.refresh(file);\n cached.times.set(file, time);\n }\n return cached.project;\n}\n\nfunction originalOffset(state: State, offset: number) {\n const position = state.generated!.mapPosition(offset);\n const point = originalPosition(state.result!, position.line, position.column);\n return point.line == null || point.column == null\n ? undefined\n : state.original.mapOffset(point.line, point.column);\n}\nfunction fix(state: State, value: Rule.Fix | undefined): Rule.Fix | undefined {\n if (!value) return undefined;\n const [start, end] = value.range;\n const offset = originalOffset(state, start);\n if (offset === undefined) return undefined;\n const position = state.original.mapPosition(offset);\n const generated = generatedPosition(state.result!, position.line, position.column);\n if (\n generated.line == null ||\n generated.column == null ||\n state.generated!.mapOffset(generated.line, generated.column) !== start ||\n state.source.slice(offset, offset + end - start) !== state.result!.code.slice(start, end)\n )\n return undefined;\n return { range: [offset, offset + end - start], text: value.text };\n}\n\nconst processor: Linter.Processor = {\n supportsAutofix: true,\n preprocess(source, filename) {\n filename = normalize(filename);\n const state: State = {\n source,\n filename,\n original: new SourcePositions(source),\n };\n pending.set(filename, state);\n try {\n // A project supplies the same inherited JSX settings as builds/checking.\n const config = ts.findConfigFile(dirname(filename), ts.sys.fileExists);\n state.result = transform(source, {\n ...loadConfig(config ? dirname(config) : dirname(filename)),\n filename,\n });\n state.generated = new SourcePositions(state.result.code);\n return [\n {\n text: state.result.code,\n filename: filename.endsWith('.twillx') ? 'source.tsx' : 'source.ts',\n },\n ];\n } catch (error) {\n state.error = error;\n return [];\n }\n },\n postprocess(lists, filename) {\n filename = normalize(filename);\n const state = pending.get(filename)!;\n pending.delete(filename);\n state.project?.update(filename);\n if (state.error)\n return [\n {\n ruleId: null,\n severity: 2,\n fatal: true,\n message: state.error.message,\n line: state.error.line ?? 1,\n column: (state.error.column ?? 0) + 1,\n },\n ];\n return lists.flat().flatMap((message) => {\n if (!message.line || !message.column) return [message];\n const generated = state.generated!;\n const start = generated.getPositionOfLineAndCharacter(message.line - 1, message.column - 1);\n let offset = originalOffset(state, start);\n let expressionSwitch = false;\n let expressionKeywordLength = 6;\n if (\n message.ruleId === '@typescript-eslint/switch-exhaustiveness-check' &&\n state.project &&\n (offset === undefined || state.result!.code[start] !== state.source[offset])\n ) {\n // Expression subjects use a generated once-evaluated local. Report the\n // missing union arm on the source-backed switch/match keyword instead.\n const file = state.project.service.getProgram()?.getSourceFile(virtualFilename(filename));\n const visit = (node: ts.Node) => {\n if (ts.isSwitchStatement(node) && node.expression.getStart() === start) {\n const sourceStart = originalOffset(state, node.getStart());\n if (\n sourceStart !== undefined &&\n /^(?:switch|match)\\b/.test(state.source.slice(sourceStart))\n ) {\n offset = sourceStart;\n expressionSwitch = true;\n expressionKeywordLength = state.source.startsWith('match', sourceStart) ? 5 : 6;\n }\n } else if (start >= node.getFullStart() && start < node.end) ts.forEachChild(node, visit);\n };\n if (file) visit(file);\n }\n if (\n offset === undefined ||\n (!expressionSwitch &&\n start < state.result!.code.length &&\n state.result!.code[start] !== state.source[offset])\n )\n return [];\n const position = state.original.getLineAndCharacterOfPosition(offset);\n const endOffset = expressionSwitch\n ? offset + expressionKeywordLength\n : message.endLine && message.endColumn\n ? originalOffset(\n state,\n generated.getPositionOfLineAndCharacter(message.endLine - 1, message.endColumn - 1),\n )\n : undefined;\n const end =\n endOffset === undefined\n ? undefined\n : state.original.getLineAndCharacterOfPosition(endOffset);\n return [\n {\n ...message,\n line: position.line + 1,\n column: position.character + 1,\n endLine: end && end.line + 1,\n endColumn: end && end.character + 1,\n fix: expressionSwitch ? undefined : fix(state, message.fix),\n // Native statement-case suggestions introduce blocks/value labels that\n // are invalid in a Twill switch expression. Keep the diagnostic.\n suggestions: expressionSwitch\n ? []\n : message.suggestions?.flatMap((suggestion) => {\n const mapped = fix(state, suggestion.fix);\n return mapped ? [{ ...suggestion, fix: mapped }] : [];\n }),\n },\n ];\n });\n },\n};\n\nconst virtualParser = {\n meta: { name: '@swiftuijs/twill-linter/parser', version: metadata.version },\n parseForESLint(code: string, options: any) {\n const original = normalize(options.filePath ?? '').replace(/\\/(?:\\d+_)?source\\.tsx?$/, '');\n const state = pending.get(original);\n if (!state) return parser.parseForESLint(code, options);\n const typed = options.projectService || options.project || options.programs;\n if (!typed) return parser.parseForESLint(code, options);\n const project = projectFor(original);\n project.update(original, state.source);\n state.project = project;\n return parser.parseForESLint(code, {\n ...options,\n filePath: virtualFilename(original),\n projectService: false,\n project: undefined,\n programs: [project.service.getProgram()!],\n });\n },\n};\n\nconst plugin: ESLint.Plugin & { configs: Record<string, Linter.Config[]> } = {\n meta: { name: '@swiftuijs/twill-linter', version: metadata.version },\n processors: { twill: processor },\n configs: {} as Record<string, Linter.Config[]>,\n};\nconst files = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'];\nfunction configuration(typed: boolean): Linter.Config[] {\n const recommended = typescriptPlugin.configs[\n typed ? 'flat/recommended-type-checked' : 'flat/recommended'\n ] as Linter.Config[];\n return [\n js.configs.recommended,\n { files: ['**/*.twill', '**/*.twillx'], plugins: { twill: plugin }, processor: 'twill/twill' },\n ...recommended.map((config) => ({\n ...config,\n files,\n languageOptions: {\n ...config.languageOptions,\n parser: virtualParser,\n ...(typed ? { parserOptions: { projectService: true } } : {}),\n },\n })),\n ...(typed\n ? [\n {\n files,\n rules: {\n '@typescript-eslint/switch-exhaustiveness-check': [\n 'error',\n { considerDefaultExhaustiveForUnions: false },\n ] satisfies Linter.RuleEntry,\n },\n },\n ]\n : []),\n ];\n}\nplugin.configs.recommended = configuration(false);\nplugin.configs.recommendedTypeChecked = configuration(true);\nexport const configs: Record<string, Linter.Config[]> = plugin.configs;\nexport const processors: Record<string, Linter.Processor> = { twill: processor };\nexport function disposeProjects() {\n for (const { project } of projects.values()) project.dispose();\n projects.clear();\n pending.clear();\n}\nexport default plugin;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftuijs/twill-linter",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,7 +24,7 @@
24
24
  "@typescript-eslint/eslint-plugin": "^8.71.0",
25
25
  "@eslint/js": "^9.39.5",
26
26
  "typescript": "5.9.3",
27
- "@swiftuijs/twill": "0.1.2"
27
+ "@swiftuijs/twill": "0.2.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "vite": "^8.3.2",