@@ -557,10 +557,16 @@ function createCustomStore() {
557
557
);
558
558
}
559
559
560
+ const m = createMemo (() => 5 ) as Accessor< number> ;
561
+
562
+ const m = createMemo (() => 5 )! ;
563
+
564
+ const m = createMemo (() => 5 )! as Accessor< number> ;
565
+
560
566
```
561
567
<!-- AUTO-GENERATED-CONTENT:END -->
562
568
563
- ### Implementation
569
+ ## Implementation
564
570
565
571
We analyze in a single pass but take advantage of ESLint's ": exit " selector
566
572
to take action both on the way down the tree and on the way back up. At any
@@ -586,3 +592,76 @@ Notes:
586
592
match a tracked scope that expects a function.
587
593
- This rule ignores classes. Solid is based on functions/closures only, and
588
594
it's uncommon to see classes with reactivity in Solid code.
595
+
596
+ ## Implementation v2 (in progress)
597
+
598
+ ` solid/reactivity ` has been public for exactly one year (!) at the time of writing, and after lots
599
+ of feedback, testing, and changes, I've noticed a few problems with its first implementation:
600
+
601
+ - ** Hard to change.** All of the data structure, analysis, and reporting code is colocated in a
602
+ single file with all the cases for detecting signals, props, and tracked scopes based on Solid
603
+ APIs. There's a few edge cases where detection code is mixed with analysis code. This makes it
604
+ hard for contributors to make PRs and hard for others to be able to maintain it.
605
+ - ** Limited to variables.** The analysis code relies heavily on ESLint's ` ScopeManager ` and scope
606
+ utilities, and therefore it can only deal with variable references, not implicitly reactive
607
+ expressions.
608
+ - ** Non-extensible.** Since detection code is hardcoded in the ` reactivity.ts ` file, it is plainly
609
+ not possible for users to mark certain APIs as reactive in some way.
610
+ - ** Susceptible to timing issues.** I'm very proud of the rule's stack-based algorithm for running
611
+ signal/props/tracked scope detection and reactivity analysis in a single pass. Its performance is
612
+ going to be extremely difficult to beat. But the single-pass approach puts complex requirements on
613
+ the detection phase—signals and props have to be marked before nested ` function:exit ` s, relying on
614
+ initialization before usage in source order. That's not a requirement I feel comfortable putting on
615
+ plugin authors, or really even myself in a few months.
616
+
617
+ So, I've decided to partially rewrite the rule with a plugin architecture to alleviate these issues.
618
+ Both the core detection code and any plugins to alter detection will use the same API.
619
+
620
+ ### Ease of change and extensibility: Plugins (Customizations)
621
+
622
+ ` solid/reactivity ` , itself part of an ESLint plugin, will support plugins of its own.
623
+
624
+ ` eslint-plugin-solid ` will expose a CLI command ` eslint-plugin-solid ` that searches
625
+ ` package.json ` files in the current working directory and its ` node_modules ` for a
626
+ ` "solid/reactivity" ` key at the top level (raw string search first for perf). This key will be expected to
627
+ contain a relative path to a CommonJS or native ESM file, accessible from requiring a subpath of the
628
+ module. For example:
629
+
630
+ ``` ts
631
+ const packageJson = { " solid/reactivity" : " ./reactivity-plugin.js" };
632
+ require .resolve (` ${packageJson .name }/${packageJson [" solid/reactivity" ]} ` );
633
+ // path to reactivity plugin
634
+ ```
635
+
636
+ The command will not run any code in ` node_modules ` ; it will just print out an example ESLint config for
637
+ the ` solid/reactivity ` rule, configured to load all plugins found. For example:
638
+
639
+ ``` json
640
+ "solid/reactivity" : [1 , {
641
+ "plugins" : [" ./node_modules/some-module-with-plugin/solid-reactivity-plugin.cjs" ]
642
+ }]
643
+ ```
644
+
645
+ This code can be inspected to ensure it matches expections, or edited to add additional paths to
646
+ more plugins. You can manually configure a particular path as a plugin without running the CLI at
647
+ all. At runtime, any plugins configured will be loaded and run alongside the base rules. Custom
648
+ hooks (` use* ` /` create* ` ) from imported from these packages will not be treated permissively, others
649
+ will.
650
+
651
+ > ` eslint-plugin-solid ` will ** not** automatically load plugins. They must be preconfigured in an
652
+ > ESLint config file.
653
+
654
+ ### Expression Support
655
+
656
+ Having detection code being moved to plugins and an API boundary lets us put reference tracking into
657
+ the analysis code, so analyzing arbitrary expressions for reactivity becomes easier. Instead of
658
+ using ` TSESLint.Scope.Reference ` s only, a custom data structure can be built to handle any ` Node ` from
659
+ the plugin API.
660
+
661
+ ### Timing
662
+
663
+ This is a good opportunity to transition from a stack-based algorithm, where information is lost
664
+ after the exit pass, to a tree-based algorithm that can capture all reactivity information in a data
665
+ structure. By using the built tree to walk through the final analysis, and colocating references
666
+ with their associated scopes, performance should stay good. The reactivity rule could then power
667
+ an editor plugin to show signals, tracked scopes, etc.
0 commit comments