@ripple-ts/eslint-plugin 0.2.202 → 0.2.204

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/dist/index.js CHANGED
@@ -37,7 +37,6 @@ const rule$6 = {
37
37
  };
38
38
  }
39
39
  };
40
- var no_module_scope_track_default = rule$6;
41
40
 
42
41
  //#endregion
43
42
  //#region src/rules/prefer-oninput.ts
@@ -85,7 +84,6 @@ const rule$5 = {
85
84
  };
86
85
  }
87
86
  };
88
- var prefer_oninput_default = rule$5;
89
87
 
90
88
  //#endregion
91
89
  //#region src/rules/no-return-in-component.ts
@@ -126,7 +124,6 @@ const rule$4 = {
126
124
  };
127
125
  }
128
126
  };
129
- var no_return_in_component_default = rule$4;
130
127
 
131
128
  //#endregion
132
129
  //#region src/rules/unbox-tracked-values.ts
@@ -174,7 +171,6 @@ const rule$3 = {
174
171
  };
175
172
  }
176
173
  };
177
- var unbox_tracked_values_default = rule$3;
178
174
 
179
175
  //#endregion
180
176
  //#region src/rules/control-flow-jsx.ts
@@ -241,7 +237,6 @@ const rule$2 = {
241
237
  };
242
238
  }
243
239
  };
244
- var control_flow_jsx_default = rule$2;
245
240
 
246
241
  //#endregion
247
242
  //#region src/rules/no-introspect-in-modules.ts
@@ -267,7 +262,6 @@ const rule$1 = {
267
262
  } };
268
263
  }
269
264
  };
270
- var no_introspect_in_modules_default = rule$1;
271
265
 
272
266
  //#endregion
273
267
  //#region src/rules/valid-for-of-key.ts
@@ -347,7 +341,6 @@ function findVariable(scope, name) {
347
341
  }
348
342
  return null;
349
343
  }
350
- var valid_for_of_key_default = rule;
351
344
 
352
345
  //#endregion
353
346
  //#region src/index.ts
@@ -357,13 +350,13 @@ const plugin = {
357
350
  version: "0.1.3"
358
351
  },
359
352
  rules: {
360
- "no-module-scope-track": no_module_scope_track_default,
361
- "prefer-oninput": prefer_oninput_default,
362
- "no-return-in-component": no_return_in_component_default,
363
- "unbox-tracked-values": unbox_tracked_values_default,
364
- "control-flow-jsx": control_flow_jsx_default,
365
- "no-introspect-in-modules": no_introspect_in_modules_default,
366
- "valid-for-of-key": valid_for_of_key_default
353
+ "no-module-scope-track": rule$6,
354
+ "prefer-oninput": rule$5,
355
+ "no-return-in-component": rule$4,
356
+ "unbox-tracked-values": rule$3,
357
+ "control-flow-jsx": rule$2,
358
+ "no-introspect-in-modules": rule$1,
359
+ "valid-for-of-key": rule
367
360
  },
368
361
  configs: {}
369
362
  };
@@ -430,8 +423,7 @@ plugin.configs.strict = [
430
423
  ]
431
424
  }
432
425
  ];
433
- var src_default = plugin;
434
426
 
435
427
  //#endregion
436
- export { src_default as default };
428
+ export { plugin as default };
437
429
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["rule","rule","rule","rule","rule","rule","noModuleScopeTrack","preferOnInput","noReturnInComponent","unboxTrackedValues","controlFlowJsx","noIntrospectInModules","validForOfKey"],"sources":["../src/rules/no-module-scope-track.ts","../src/rules/prefer-oninput.ts","../src/rules/no-return-in-component.ts","../src/rules/unbox-tracked-values.ts","../src/rules/control-flow-jsx.ts","../src/rules/no-introspect-in-modules.ts","../src/rules/valid-for-of-key.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\nimport type { CallExpression } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow calling track() at module scope',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n moduleScope: 'track() cannot be called at module scope. It must be called within a component context.',\n },\n schema: [],\n },\n create(context) {\n let componentDepth = 0;\n let functionDepth = 0;\n\n const incrementComponentDepth = () => componentDepth++;\n const decrementComponentDepth = () => componentDepth--;\n const incrementFunctionDepth = () => functionDepth++;\n const decrementFunctionDepth = () => functionDepth--;\n\n return {\n // Only track when we enter a Ripple component\n // Ripple's parser returns \"Component\" nodes for component declarations\n 'Component': incrementComponentDepth,\n 'Component:exit': decrementComponentDepth,\n\n // Track regular functions and arrow functions\n 'FunctionDeclaration': incrementFunctionDepth,\n 'FunctionDeclaration:exit': decrementFunctionDepth,\n 'FunctionExpression': incrementFunctionDepth,\n 'FunctionExpression:exit': decrementFunctionDepth,\n 'ArrowFunctionExpression': incrementFunctionDepth,\n 'ArrowFunctionExpression:exit': decrementFunctionDepth,\n\n // Check track() calls\n CallExpression(node: CallExpression) {\n if (\n node.callee.type === 'Identifier' &&\n node.callee.name === 'track' &&\n componentDepth === 0 &&\n functionDepth === 0\n ) {\n context.report({\n node,\n messageId: 'moduleScope',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Prefer onInput over onChange for form inputs in Ripple',\n category: 'Best Practices',\n recommended: true,\n },\n messages: {\n preferOnInput: 'Use \"onInput\" instead of \"onChange\". Ripple does not have synthetic events like React.',\n },\n fixable: 'code',\n schema: [],\n },\n create(context) {\n return {\n // Check JSX attributes (standard JSX)\n 'JSXAttribute[name.name=\"onChange\"]'(node: any) {\n context.report({\n node,\n messageId: 'preferOnInput',\n fix(fixer) {\n return fixer.replaceText(node.name, 'onInput');\n },\n });\n },\n // Check Attribute nodes (Ripple parser)\n 'Attribute[name.name=\"onChange\"]'(node: any) {\n context.report({\n node,\n messageId: 'preferOnInput',\n fix(fixer) {\n return fixer.replaceText(node.name, 'onInput');\n },\n });\n },\n // Check object properties (for spread props)\n 'Property[key.name=\"onChange\"]'(node: any) {\n // Only report if this looks like it's in a props object\n const ancestors = context.sourceCode.getAncestors(node);\n const inObjectExpression = ancestors.some(\n (ancestor) => ancestor.type === 'ObjectExpression'\n );\n\n if (inObjectExpression) {\n context.report({\n node,\n messageId: 'preferOnInput',\n fix(fixer) {\n return fixer.replaceText(node.key, 'onInput');\n },\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type { ReturnStatement } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow return statements with JSX in Ripple components',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n noReturn: 'Do not return JSX from Ripple components. Use JSX as statements instead.',\n },\n schema: [],\n },\n create(context) {\n let insideComponent = 0;\n\n return {\n // Track component boundaries\n \"ExpressionStatement > CallExpression[callee.name='component']\"() {\n insideComponent++;\n },\n \"ExpressionStatement > CallExpression[callee.name='component']:exit\"() {\n insideComponent--;\n },\n // Also track arrow functions and regular functions that might be components\n 'VariableDeclarator[init.callee.name=\"component\"]'() {\n insideComponent++;\n },\n 'VariableDeclarator[init.callee.name=\"component\"]:exit'() {\n insideComponent--;\n },\n // Check return statements\n ReturnStatement(node: ReturnStatement) {\n if (insideComponent > 0 && node.argument) {\n // Check if returning JSX (JSXElement, JSXFragment)\n if (\n node.argument.type === 'JSXElement' ||\n node.argument.type === 'JSXFragment'\n ) {\n context.report({\n node,\n messageId: 'noReturn',\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Ensure tracked values are unboxed with @ operator',\n\t\t\tcategory: 'Possible Errors',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tneedsUnbox: 'Tracked value should be unboxed with @ operator. Did you mean \"@{{name}}\"?',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst trackedVariables = new Set<string>();\n\n\t\tfunction isInJSXContext(node: any): boolean {\n\t\t\tlet parent = node.parent;\n\n\t\t\t// Walk up the AST to find if we're inside JSX/Element\n\t\t\twhile (parent) {\n\t\t\t\tconst parentType = parent.type;\n\t\t\t\t// Check for JSX context\n\t\t\t\tif (\n\t\t\t\t\tparentType === 'JSXExpressionContainer' ||\n\t\t\t\t\tparentType === 'JSXElement' ||\n\t\t\t\t\tparentType === 'JSXFragment' ||\n\t\t\t\t\t// Check for Ripple Element context\n\t\t\t\t\tparentType === 'ExpressionContainer' ||\n\t\t\t\t\tparentType === 'Element'\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tparent = parent.parent;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction checkTrackedIdentifier(node: any) {\n\t\t\tif (trackedVariables.has(node.name) && isInJSXContext(node)) {\n\t\t\t\tconst parent = node.parent;\n\t\t\t\tlet isUnboxed = (parent && parent.type === 'TrackedExpression') || node.tracked === true;\n\n\t\t\t\t// Fallback: check source code for @ character as the first character\n\t\t\t\tif (!isUnboxed) {\n\t\t\t\t\tconst sourceCode = context.getSourceCode();\n\t\t\t\t\tconst firstChar = sourceCode.text.substring(Math.max(0, node.range![0]), node.range![0]);\n\t\t\t\t\tisUnboxed = firstChar === '@';\n\t\t\t\t}\n\n\t\t\t\tif (!isUnboxed) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'needsUnbox',\n\t\t\t\t\t\tdata: { name: node.name },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t// Track variables that are assigned from track()\n\t\t\t'VariableDeclarator[init.callee.name=\"track\"]'(node: any) {\n\t\t\t\tif (node.id.type === 'Identifier') {\n\t\t\t\t\ttrackedVariables.add(node.id.name);\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Check all identifiers\n\t\t\tIdentifier(node: any) {\n\t\t\t\tcheckTrackedIdentifier(node);\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type { ForOfStatement, Node } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require JSX in for...of loops within components, but disallow JSX in for...of loops within effects',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n requireJsxInLoop:\n 'For...of loops in component bodies should contain JSX elements. Use JSX to render items.',\n noJsxInEffectLoop:\n 'For...of loops inside effect() should not contain JSX. Effects are for side effects, not rendering.',\n },\n schema: [],\n },\n create(context) {\n let insideComponent = 0;\n let insideEffect = 0;\n\n function containsJSX(node: Node, visited: Set<Node> = new Set()): boolean {\n if (!node) return false;\n\n // Avoid infinite loops from circular references\n if (visited.has(node)) return false;\n visited.add(node);\n\n // Check if current node is JSX/Element (Ripple uses 'Element' type instead of 'JSXElement')\n if (node.type === 'JSXElement' as string || node.type === 'JSXFragment' as string || node.type === 'Element' as string) {\n return true;\n }\n\n const keys = Object.keys(node);\n for (const key of keys) {\n if (key === 'parent' || key === 'loc' || key === 'range') {\n continue;\n }\n\n const value = (node as any)[key];\n if (value && typeof value === 'object') {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item && typeof item === 'object' && containsJSX(item, visited)) {\n return true;\n }\n }\n } else if (value.type && containsJSX(value, visited)) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n return {\n Component() {\n insideComponent++;\n },\n 'Component:exit'() {\n insideComponent--;\n },\n\n \"CallExpression[callee.name='effect']\"() {\n insideEffect++;\n },\n \"CallExpression[callee.name='effect']:exit\"() {\n insideEffect--;\n },\n\n ForOfStatement(node: ForOfStatement) {\n if (insideComponent === 0) return;\n\n const hasJSX = containsJSX(node.body);\n\n if (insideEffect > 0) {\n if (hasJSX) {\n context.report({\n node,\n messageId: 'noJsxInEffectLoop',\n });\n }\n } else {\n if (!hasJSX) {\n context.report({\n node,\n messageId: 'requireJsxInLoop',\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow @ introspection operator in TypeScript/JavaScript modules',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n noIntrospect: 'The @ operator cannot be used in TypeScript/JavaScript modules. Use get() to read tracked values and set() to update them instead.',\n },\n schema: [],\n },\n create(context) {\n const filename = context.filename || context.getFilename();\n \n // Skip .ripple files where @ operator is valid\n if (filename && filename.endsWith('.ripple')) {\n return {};\n }\n\n return {\n // Check for identifiers with the 'tracked' property\n // The @ operator is parsed by Ripple as an identifier with tracked=true\n Identifier(node: any) {\n if (node.tracked === true) {\n context.report({\n node,\n messageId: 'noIntrospect',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Ensure variables used in for..of key expression are defined',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tundefinedVariable: \"Variable '{{name}}' is not defined.\",\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\treturn {\n\t\t\tForOfStatement(node: any) {\n\t\t\t\tif (!node.key) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst checkIdentifier = (identifier: any) => {\n\t\t\t\t\tconst scope = context.sourceCode.getScope(node);\n\t\t\t\t\tconst variable = findVariable(scope, identifier.name);\n\n\t\t\t\t\tif (!variable) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode: identifier,\n\t\t\t\t\t\t\tmessageId: 'undefinedVariable',\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tname: identifier.name,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst traverse = (node: any) => {\n\t\t\t\t\tif (!node) return;\n\n\t\t\t\t\tswitch (node.type) {\n\t\t\t\t\t\tcase 'Identifier':\n\t\t\t\t\t\t\tcheckIdentifier(node);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'MemberExpression':\n\t\t\t\t\t\t\ttraverse(node.object);\n\n\t\t\t\t\t\t\tif (node.computed) {\n\t\t\t\t\t\t\t\ttraverse(node.property);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'BinaryExpression':\n\t\t\t\t\t\tcase 'LogicalExpression':\n\t\t\t\t\t\t\ttraverse(node.left);\n\t\t\t\t\t\t\ttraverse(node.right);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'UnaryExpression':\n\t\t\t\t\t\t\ttraverse(node.argument);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'CallExpression':\n\t\t\t\t\t\t\ttraverse(node.callee);\n\t\t\t\t\t\t\tnode.arguments.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ArrayExpression':\n\t\t\t\t\t\t\tnode.elements.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ObjectExpression':\n\t\t\t\t\t\t\tnode.properties.forEach((prop: any) => {\n\t\t\t\t\t\t\t\tif (prop.type === 'Property') {\n\t\t\t\t\t\t\t\t\tif (prop.computed) {\n\t\t\t\t\t\t\t\t\t\ttraverse(prop.key);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\ttraverse(prop.value);\n\t\t\t\t\t\t\t\t} else if (prop.type === 'SpreadElement') {\n\t\t\t\t\t\t\t\t\ttraverse(prop.argument);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ConditionalExpression':\n\t\t\t\t\t\t\ttraverse(node.test);\n\t\t\t\t\t\t\ttraverse(node.consequent);\n\t\t\t\t\t\t\ttraverse(node.alternate);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'TemplateLiteral':\n\t\t\t\t\t\t\tnode.expressions.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\ttraverse(node.key);\n\t\t\t},\n\t\t};\n\t},\n};\n\nfunction findVariable(scope: any, name: string) {\n\tlet currentScope = scope;\n\n\twhile (currentScope) {\n\t\tconst variable = currentScope.variables.find((v: { name: string }) => v.name === name);\n\n\t\tif (variable) {\n\t\t\treturn variable;\n\t\t}\n\n\t\t// Also check references for global variables or variables defined in the loop itself (like the loop variable)\n\t\t// The loop variable might not be in the 'variables' list of the surrounding scope yet if we are inside the loop statement\n\t\t// But for 'for-of', the loop variable is in a special scope or the upper scope.\n\t\t// Let's rely on standard scope analysis.\n\n\t\t// Special case: check if the variable is the loop variable itself (left side of for-of)\n\t\t// The scope analysis might handle this, but let's be sure.\n\n\t\tcurrentScope = currentScope.upper;\n\t}\n\n\t// If not found in scopes, it might be a global variable.\n\t// ESLint scope analysis usually includes globals in the global scope.\n\t// However, if we are in a module, we might need to check if it's an implicit global or defined elsewhere.\n\t// For now, let's assume standard scope resolution works.\n\n\treturn null;\n}\n\nexport default rule;\n","import { createRequire } from 'module';\nimport noModuleScopeTrack from './rules/no-module-scope-track.js';\nimport preferOnInput from './rules/prefer-oninput.js';\nimport noReturnInComponent from './rules/no-return-in-component.js';\nimport unboxTrackedValues from './rules/unbox-tracked-values.js';\nimport controlFlowJsx from './rules/control-flow-jsx.js';\nimport noIntrospectInModules from './rules/no-introspect-in-modules.js';\nimport validForOfKey from './rules/valid-for-of-key.js';\n\nconst plugin = {\n\tmeta: {\n\t\tname: '@ripple-ts/eslint-plugin',\n\t\tversion: '0.1.3',\n\t},\n\trules: {\n\t\t'no-module-scope-track': noModuleScopeTrack,\n\t\t'prefer-oninput': preferOnInput,\n\t\t'no-return-in-component': noReturnInComponent,\n\t\t'unbox-tracked-values': unboxTrackedValues,\n\t\t'control-flow-jsx': controlFlowJsx,\n\t\t'no-introspect-in-modules': noIntrospectInModules,\n\t\t'valid-for-of-key': validForOfKey,\n\t},\n\tconfigs: {} as any,\n};\n\n// Try to load optional parsers\nconst require = createRequire(import.meta.url);\n\nlet rippleParser: any;\nlet tsParser: any;\n\ntry {\n\trippleParser = require('@ripple-ts/eslint-parser');\n} catch {\n\t// @ripple-ts/eslint-parser is optional\n\trippleParser = null;\n}\n\ntry {\n\ttsParser = require('@typescript-eslint/parser');\n} catch {\n\t// @typescript-eslint/parser is optional\n\ttsParser = null;\n}\n\n// Helper to create config objects\nfunction createConfig(name: string, files: string[], parser: any) {\n\tconst config: any = {\n\t\tname,\n\t\tfiles,\n\t\tplugins: {\n\t\t\tripple: plugin,\n\t\t},\n\t\trules: {\n\t\t\t'ripple/no-module-scope-track': 'error',\n\t\t\t'ripple/prefer-oninput': 'warn',\n\t\t\t'ripple/no-return-in-component': 'error',\n\t\t\t'ripple/unbox-tracked-values': 'error',\n\t\t\t'ripple/control-flow-jsx': 'error',\n\t\t\t'ripple/no-introspect-in-modules': 'error',\n\t\t\t'ripple/valid-for-of-key': 'error',\n\t\t},\n\t};\n\n\t// Only add parser if it's available\n\tif (parser) {\n\t\tconfig.languageOptions = {\n\t\t\tparser,\n\t\t\tparserOptions: {\n\t\t\t\tecmaVersion: 'latest',\n\t\t\t\tsourceType: 'module',\n\t\t\t},\n\t\t};\n\t}\n\n\treturn config;\n}\n\n// Recommended configuration (flat config format)\nplugin.configs.recommended = [\n\tcreateConfig('ripple/recommended-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/recommended-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\n// Strict configuration (flat config format)\nplugin.configs.strict = [\n\tcreateConfig('ripple/strict-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/strict-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\nexport default plugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["rule","rule","rule","rule","rule","rule","noModuleScopeTrack","preferOnInput","noReturnInComponent","unboxTrackedValues","controlFlowJsx","noIntrospectInModules","validForOfKey"],"sources":["../src/rules/no-module-scope-track.ts","../src/rules/prefer-oninput.ts","../src/rules/no-return-in-component.ts","../src/rules/unbox-tracked-values.ts","../src/rules/control-flow-jsx.ts","../src/rules/no-introspect-in-modules.ts","../src/rules/valid-for-of-key.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\nimport type { CallExpression } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow calling track() at module scope',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n moduleScope: 'track() cannot be called at module scope. It must be called within a component context.',\n },\n schema: [],\n },\n create(context) {\n let componentDepth = 0;\n let functionDepth = 0;\n\n const incrementComponentDepth = () => componentDepth++;\n const decrementComponentDepth = () => componentDepth--;\n const incrementFunctionDepth = () => functionDepth++;\n const decrementFunctionDepth = () => functionDepth--;\n\n return {\n // Only track when we enter a Ripple component\n // Ripple's parser returns \"Component\" nodes for component declarations\n 'Component': incrementComponentDepth,\n 'Component:exit': decrementComponentDepth,\n\n // Track regular functions and arrow functions\n 'FunctionDeclaration': incrementFunctionDepth,\n 'FunctionDeclaration:exit': decrementFunctionDepth,\n 'FunctionExpression': incrementFunctionDepth,\n 'FunctionExpression:exit': decrementFunctionDepth,\n 'ArrowFunctionExpression': incrementFunctionDepth,\n 'ArrowFunctionExpression:exit': decrementFunctionDepth,\n\n // Check track() calls\n CallExpression(node: CallExpression) {\n if (\n node.callee.type === 'Identifier' &&\n node.callee.name === 'track' &&\n componentDepth === 0 &&\n functionDepth === 0\n ) {\n context.report({\n node,\n messageId: 'moduleScope',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Prefer onInput over onChange for form inputs in Ripple',\n category: 'Best Practices',\n recommended: true,\n },\n messages: {\n preferOnInput: 'Use \"onInput\" instead of \"onChange\". Ripple does not have synthetic events like React.',\n },\n fixable: 'code',\n schema: [],\n },\n create(context) {\n return {\n // Check JSX attributes (standard JSX)\n 'JSXAttribute[name.name=\"onChange\"]'(node: any) {\n context.report({\n node,\n messageId: 'preferOnInput',\n fix(fixer) {\n return fixer.replaceText(node.name, 'onInput');\n },\n });\n },\n // Check Attribute nodes (Ripple parser)\n 'Attribute[name.name=\"onChange\"]'(node: any) {\n context.report({\n node,\n messageId: 'preferOnInput',\n fix(fixer) {\n return fixer.replaceText(node.name, 'onInput');\n },\n });\n },\n // Check object properties (for spread props)\n 'Property[key.name=\"onChange\"]'(node: any) {\n // Only report if this looks like it's in a props object\n const ancestors = context.sourceCode.getAncestors(node);\n const inObjectExpression = ancestors.some(\n (ancestor) => ancestor.type === 'ObjectExpression'\n );\n\n if (inObjectExpression) {\n context.report({\n node,\n messageId: 'preferOnInput',\n fix(fixer) {\n return fixer.replaceText(node.key, 'onInput');\n },\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type { ReturnStatement } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow return statements with JSX in Ripple components',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n noReturn: 'Do not return JSX from Ripple components. Use JSX as statements instead.',\n },\n schema: [],\n },\n create(context) {\n let insideComponent = 0;\n\n return {\n // Track component boundaries\n \"ExpressionStatement > CallExpression[callee.name='component']\"() {\n insideComponent++;\n },\n \"ExpressionStatement > CallExpression[callee.name='component']:exit\"() {\n insideComponent--;\n },\n // Also track arrow functions and regular functions that might be components\n 'VariableDeclarator[init.callee.name=\"component\"]'() {\n insideComponent++;\n },\n 'VariableDeclarator[init.callee.name=\"component\"]:exit'() {\n insideComponent--;\n },\n // Check return statements\n ReturnStatement(node: ReturnStatement) {\n if (insideComponent > 0 && node.argument) {\n // Check if returning JSX (JSXElement, JSXFragment)\n if (\n node.argument.type === 'JSXElement' ||\n node.argument.type === 'JSXFragment'\n ) {\n context.report({\n node,\n messageId: 'noReturn',\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Ensure tracked values are unboxed with @ operator',\n\t\t\tcategory: 'Possible Errors',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tneedsUnbox: 'Tracked value should be unboxed with @ operator. Did you mean \"@{{name}}\"?',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst trackedVariables = new Set<string>();\n\n\t\tfunction isInJSXContext(node: any): boolean {\n\t\t\tlet parent = node.parent;\n\n\t\t\t// Walk up the AST to find if we're inside JSX/Element\n\t\t\twhile (parent) {\n\t\t\t\tconst parentType = parent.type;\n\t\t\t\t// Check for JSX context\n\t\t\t\tif (\n\t\t\t\t\tparentType === 'JSXExpressionContainer' ||\n\t\t\t\t\tparentType === 'JSXElement' ||\n\t\t\t\t\tparentType === 'JSXFragment' ||\n\t\t\t\t\t// Check for Ripple Element context\n\t\t\t\t\tparentType === 'ExpressionContainer' ||\n\t\t\t\t\tparentType === 'Element'\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tparent = parent.parent;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction checkTrackedIdentifier(node: any) {\n\t\t\tif (trackedVariables.has(node.name) && isInJSXContext(node)) {\n\t\t\t\tconst parent = node.parent;\n\t\t\t\tlet isUnboxed = (parent && parent.type === 'TrackedExpression') || node.tracked === true;\n\n\t\t\t\t// Fallback: check source code for @ character as the first character\n\t\t\t\tif (!isUnboxed) {\n\t\t\t\t\tconst sourceCode = context.getSourceCode();\n\t\t\t\t\tconst firstChar = sourceCode.text.substring(Math.max(0, node.range![0]), node.range![0]);\n\t\t\t\t\tisUnboxed = firstChar === '@';\n\t\t\t\t}\n\n\t\t\t\tif (!isUnboxed) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'needsUnbox',\n\t\t\t\t\t\tdata: { name: node.name },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t// Track variables that are assigned from track()\n\t\t\t'VariableDeclarator[init.callee.name=\"track\"]'(node: any) {\n\t\t\t\tif (node.id.type === 'Identifier') {\n\t\t\t\t\ttrackedVariables.add(node.id.name);\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Check all identifiers\n\t\t\tIdentifier(node: any) {\n\t\t\t\tcheckTrackedIdentifier(node);\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type { ForOfStatement, Node } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require JSX in for...of loops within components, but disallow JSX in for...of loops within effects',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n requireJsxInLoop:\n 'For...of loops in component bodies should contain JSX elements. Use JSX to render items.',\n noJsxInEffectLoop:\n 'For...of loops inside effect() should not contain JSX. Effects are for side effects, not rendering.',\n },\n schema: [],\n },\n create(context) {\n let insideComponent = 0;\n let insideEffect = 0;\n\n function containsJSX(node: Node, visited: Set<Node> = new Set()): boolean {\n if (!node) return false;\n\n // Avoid infinite loops from circular references\n if (visited.has(node)) return false;\n visited.add(node);\n\n // Check if current node is JSX/Element (Ripple uses 'Element' type instead of 'JSXElement')\n if (node.type === 'JSXElement' as string || node.type === 'JSXFragment' as string || node.type === 'Element' as string) {\n return true;\n }\n\n const keys = Object.keys(node);\n for (const key of keys) {\n if (key === 'parent' || key === 'loc' || key === 'range') {\n continue;\n }\n\n const value = (node as any)[key];\n if (value && typeof value === 'object') {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item && typeof item === 'object' && containsJSX(item, visited)) {\n return true;\n }\n }\n } else if (value.type && containsJSX(value, visited)) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n return {\n Component() {\n insideComponent++;\n },\n 'Component:exit'() {\n insideComponent--;\n },\n\n \"CallExpression[callee.name='effect']\"() {\n insideEffect++;\n },\n \"CallExpression[callee.name='effect']:exit\"() {\n insideEffect--;\n },\n\n ForOfStatement(node: ForOfStatement) {\n if (insideComponent === 0) return;\n\n const hasJSX = containsJSX(node.body);\n\n if (insideEffect > 0) {\n if (hasJSX) {\n context.report({\n node,\n messageId: 'noJsxInEffectLoop',\n });\n }\n } else {\n if (!hasJSX) {\n context.report({\n node,\n messageId: 'requireJsxInLoop',\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow @ introspection operator in TypeScript/JavaScript modules',\n category: 'Possible Errors',\n recommended: true,\n },\n messages: {\n noIntrospect: 'The @ operator cannot be used in TypeScript/JavaScript modules. Use get() to read tracked values and set() to update them instead.',\n },\n schema: [],\n },\n create(context) {\n const filename = context.filename || context.getFilename();\n \n // Skip .ripple files where @ operator is valid\n if (filename && filename.endsWith('.ripple')) {\n return {};\n }\n\n return {\n // Check for identifiers with the 'tracked' property\n // The @ operator is parsed by Ripple as an identifier with tracked=true\n Identifier(node: any) {\n if (node.tracked === true) {\n context.report({\n node,\n messageId: 'noIntrospect',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Ensure variables used in for..of key expression are defined',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tundefinedVariable: \"Variable '{{name}}' is not defined.\",\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\treturn {\n\t\t\tForOfStatement(node: any) {\n\t\t\t\tif (!node.key) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst checkIdentifier = (identifier: any) => {\n\t\t\t\t\tconst scope = context.sourceCode.getScope(node);\n\t\t\t\t\tconst variable = findVariable(scope, identifier.name);\n\n\t\t\t\t\tif (!variable) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode: identifier,\n\t\t\t\t\t\t\tmessageId: 'undefinedVariable',\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tname: identifier.name,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst traverse = (node: any) => {\n\t\t\t\t\tif (!node) return;\n\n\t\t\t\t\tswitch (node.type) {\n\t\t\t\t\t\tcase 'Identifier':\n\t\t\t\t\t\t\tcheckIdentifier(node);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'MemberExpression':\n\t\t\t\t\t\t\ttraverse(node.object);\n\n\t\t\t\t\t\t\tif (node.computed) {\n\t\t\t\t\t\t\t\ttraverse(node.property);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'BinaryExpression':\n\t\t\t\t\t\tcase 'LogicalExpression':\n\t\t\t\t\t\t\ttraverse(node.left);\n\t\t\t\t\t\t\ttraverse(node.right);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'UnaryExpression':\n\t\t\t\t\t\t\ttraverse(node.argument);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'CallExpression':\n\t\t\t\t\t\t\ttraverse(node.callee);\n\t\t\t\t\t\t\tnode.arguments.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ArrayExpression':\n\t\t\t\t\t\t\tnode.elements.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ObjectExpression':\n\t\t\t\t\t\t\tnode.properties.forEach((prop: any) => {\n\t\t\t\t\t\t\t\tif (prop.type === 'Property') {\n\t\t\t\t\t\t\t\t\tif (prop.computed) {\n\t\t\t\t\t\t\t\t\t\ttraverse(prop.key);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\ttraverse(prop.value);\n\t\t\t\t\t\t\t\t} else if (prop.type === 'SpreadElement') {\n\t\t\t\t\t\t\t\t\ttraverse(prop.argument);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ConditionalExpression':\n\t\t\t\t\t\t\ttraverse(node.test);\n\t\t\t\t\t\t\ttraverse(node.consequent);\n\t\t\t\t\t\t\ttraverse(node.alternate);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'TemplateLiteral':\n\t\t\t\t\t\t\tnode.expressions.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\ttraverse(node.key);\n\t\t\t},\n\t\t};\n\t},\n};\n\nfunction findVariable(scope: any, name: string) {\n\tlet currentScope = scope;\n\n\twhile (currentScope) {\n\t\tconst variable = currentScope.variables.find((v: { name: string }) => v.name === name);\n\n\t\tif (variable) {\n\t\t\treturn variable;\n\t\t}\n\n\t\t// Also check references for global variables or variables defined in the loop itself (like the loop variable)\n\t\t// The loop variable might not be in the 'variables' list of the surrounding scope yet if we are inside the loop statement\n\t\t// But for 'for-of', the loop variable is in a special scope or the upper scope.\n\t\t// Let's rely on standard scope analysis.\n\n\t\t// Special case: check if the variable is the loop variable itself (left side of for-of)\n\t\t// The scope analysis might handle this, but let's be sure.\n\n\t\tcurrentScope = currentScope.upper;\n\t}\n\n\t// If not found in scopes, it might be a global variable.\n\t// ESLint scope analysis usually includes globals in the global scope.\n\t// However, if we are in a module, we might need to check if it's an implicit global or defined elsewhere.\n\t// For now, let's assume standard scope resolution works.\n\n\treturn null;\n}\n\nexport default rule;\n","import { createRequire } from 'module';\nimport noModuleScopeTrack from './rules/no-module-scope-track.js';\nimport preferOnInput from './rules/prefer-oninput.js';\nimport noReturnInComponent from './rules/no-return-in-component.js';\nimport unboxTrackedValues from './rules/unbox-tracked-values.js';\nimport controlFlowJsx from './rules/control-flow-jsx.js';\nimport noIntrospectInModules from './rules/no-introspect-in-modules.js';\nimport validForOfKey from './rules/valid-for-of-key.js';\n\nconst plugin = {\n\tmeta: {\n\t\tname: '@ripple-ts/eslint-plugin',\n\t\tversion: '0.1.3',\n\t},\n\trules: {\n\t\t'no-module-scope-track': noModuleScopeTrack,\n\t\t'prefer-oninput': preferOnInput,\n\t\t'no-return-in-component': noReturnInComponent,\n\t\t'unbox-tracked-values': unboxTrackedValues,\n\t\t'control-flow-jsx': controlFlowJsx,\n\t\t'no-introspect-in-modules': noIntrospectInModules,\n\t\t'valid-for-of-key': validForOfKey,\n\t},\n\tconfigs: {} as any,\n};\n\n// Try to load optional parsers\nconst require = createRequire(import.meta.url);\n\nlet rippleParser: any;\nlet tsParser: any;\n\ntry {\n\trippleParser = require('@ripple-ts/eslint-parser');\n} catch {\n\t// @ripple-ts/eslint-parser is optional\n\trippleParser = null;\n}\n\ntry {\n\ttsParser = require('@typescript-eslint/parser');\n} catch {\n\t// @typescript-eslint/parser is optional\n\ttsParser = null;\n}\n\n// Helper to create config objects\nfunction createConfig(name: string, files: string[], parser: any) {\n\tconst config: any = {\n\t\tname,\n\t\tfiles,\n\t\tplugins: {\n\t\t\tripple: plugin,\n\t\t},\n\t\trules: {\n\t\t\t'ripple/no-module-scope-track': 'error',\n\t\t\t'ripple/prefer-oninput': 'warn',\n\t\t\t'ripple/no-return-in-component': 'error',\n\t\t\t'ripple/unbox-tracked-values': 'error',\n\t\t\t'ripple/control-flow-jsx': 'error',\n\t\t\t'ripple/no-introspect-in-modules': 'error',\n\t\t\t'ripple/valid-for-of-key': 'error',\n\t\t},\n\t};\n\n\t// Only add parser if it's available\n\tif (parser) {\n\t\tconfig.languageOptions = {\n\t\t\tparser,\n\t\t\tparserOptions: {\n\t\t\t\tecmaVersion: 'latest',\n\t\t\t\tsourceType: 'module',\n\t\t\t},\n\t\t};\n\t}\n\n\treturn config;\n}\n\n// Recommended configuration (flat config format)\nplugin.configs.recommended = [\n\tcreateConfig('ripple/recommended-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/recommended-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\n// Strict configuration (flat config format)\nplugin.configs.strict = [\n\tcreateConfig('ripple/strict-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/strict-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\nexport default plugin;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ripple-ts/eslint-plugin",
3
- "version": "0.2.202",
3
+ "version": "0.2.204",
4
4
  "description": "ESLint plugin for Ripple",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -30,7 +30,7 @@
30
30
  "peerDependencies": {
31
31
  "eslint": ">=9.0.0",
32
32
  "@typescript-eslint/parser": "^8.20.0",
33
- "@ripple-ts/eslint-parser": "0.2.202"
33
+ "@ripple-ts/eslint-parser": "0.2.204"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/eslint": "^9.6.1",
@@ -41,7 +41,7 @@
41
41
  "tsdown": "^0.15.4",
42
42
  "typescript": "^5.9.2",
43
43
  "vitest": "^3.2.4",
44
- "@ripple-ts/eslint-parser": "0.2.202"
44
+ "@ripple-ts/eslint-parser": "0.2.204"
45
45
  },
46
46
  "engines": {
47
47
  "node": ">=20.0.0"