Coder Social home page Coder Social logo

marcoroth / stimulus-parser Goto Github PK

View Code? Open in Web Editor NEW
30.0 1.0 7.0 1.34 MB

Statically analyze Stimulus controllers in your project.

Home Page: https://hotwire.io/ecosystem/tooling/stimulus-parser

License: MIT License

TypeScript 97.11% JavaScript 1.75% HTML 1.10% CSS 0.04%
hotwire parser static-analysis stimulus hacktoberfest

stimulus-parser's Introduction

stimulus-parser

NPM Version NPM Downloads NPM Bundle Size

Installation

To add stimulus-parser to your project, run the following command in your terminal:

yarn add stimulus-parser

Usage

import { Project } from "stimulus-parser"

const project = new Project("/Users/user/path/to/project")

const controllers = project.controllerDefinitions
const controller = controllers[0]

console.log(controller.actionNames)
// => ["connect", "click", "disconnect"]

console.log(controller.targetNames)
// => ["name", "output"]

console.log(controller.classNames)
// => ["loading"]

console.log(controller.values)
// => [{ url: { type: "String", default: "" } }]

Playground

You can inspect parse results on the hosted playground at https://stimulus-parser.hotwire.io.

Development

To run the tests:

yarn install
yarn build
yarn test

stimulus-parser's People

Contributors

dependabot[bot] avatar djfpaagman avatar marcoroth avatar nachiket87 avatar tonysm avatar zeko369 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

stimulus-parser's Issues

Support inheritance for `methods`, `targets`, `values`, etc.

It's common for Stimulus controllers to inherit some behaviour from an (abstract) base controller.

Currently the inherited methods, targets, values, etc. aren't provided on the child controller.

Like:

// base_controller.js

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["base"]
}
// specfic_controller.js

import BaseController from "./base_controller"

export default class extends BaseController {
  static targets = ["specific"]
}

The specific controller should now have both the base and specific target.

Support detecting controllers that don't end with the `_controller` suffix in the filename

Most people follow the convention of having the controllers in a **/**/controllers/ folder and having the filename end with the _controller.{js,ts} suffix. Though there are still cases where people don't or cannot follow this convention.

In order to still support that we would need to look into all *.js or *.ts in a project and check if they somehow reference Stimulus controllers.

Public class fields without a default value break the parser

I was playing around with the LSP and noticed errors for some of my Stimulus controllers.

Debugging a bit I noticed that the parser fails on them, because we use some empty public class fields like so:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  someField;

  // this is just an example how we use them later, not related to the bug
  doSomething() {
    this.someField = "some value";
  }
}
  • It does work if you set it with a value, like someField = "initial value".
  • Having static in front of it also breaks

It fails on this line:

if (node.value.type === "ArrowFunctionExpression") {
with TypeError: Cannot read properties of null (reading 'type')

I guess those nodes don't have a type, so some other parsing exception should be added.

Happy to help, but I don't have a good idea in what direction to go.

edit later that day: I made a PR that implements a possible fix in #21!

Using the defintionsFromContext webpack helper does not mark controllers as registered

We are using Debian Rails which is at 6.1 for the time being. Note that this Rails version isn't deprecated and will continue to receive security fixes even after Rails stops supporting it, unless there is a new stable Debian.

Using definitionsFromContext from the @hotwired/stimulus-webpack-helpers package does not mark Stimulus controllers as registered.

import { definitionsFromContext } from '@hotwired/stimulus-webpack-helpers';

const application = Application.start(document.documentElement);

// Normally you'd need to add the webpack env types, but since we'll migrate
// away (in Rails 7), and we know the signatures here, it's ignored and casted
// as any instead.

// @ts-ignore
// eslint-disable-next-line no-undef
const context = require.context('../controllers', true, /_controller\.ts$/);
application.load(definitionsFromContext(context));

declare const window: Window & { Stimulus: Application };
window.Stimulus = application;

The result in the Stimulus Controllers view:

52 controllers showing as unregistered

I don't know if this is related, but I don't see any autocompletion in .html.erb files. I would expect them to not show for unregistered controllers, hence me opening this issue.

Trying to circumvent it by using the Register controller definition on the stimulus application yields the following error message:

Cannot read properties of undefined (reading 'isImportable')

Function `analyzeStaticPropertiesExpressions` needs to take `object` into account

Currently, given this file, the targets ["name", "output"] will be assigned to both classes

import { Controller } from "@hotwired/stimulus"

class One extends Controller {}
class Two extends Controller {}

One.targets = ["name", "output"]

Playground: https://stimulus-parser.hotwire.io/#JYWwDg9gTgLgBAbzgYQgOxlCAbbBTKOAXzgDMsQ4AiAAQAsIYB3YKPAEwHoBnGUAV2z9uVAFCiAxtgCG3bnADyaPHDwAPGHjTt5qDFlwFERSTLlwAKkwiqNWnSnSYc+QghOileAHQxpUAHM8GHkAXjgAbSo0aRA8KgAaagh+GDBUqgBdIA

Error parsing private getters

An unexpected token error will be raised if a Stimulus controller has a private getter defined, such as:

get #activeItems () {
    return this.itemTargets.filter(div => !div.classList.contains('destroying'))
}

Consider Typescript Parser

Acorn might not be the best choice here. While slower and harder to reason with, the TypeScript parser might be the easier route to take in order to obtain lexical context of each controller.

Referencing #9

Parse kind of value definition

Shorthand-version:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static values = {
    name: String
  }
}

Explicit-version:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static values = {
    name: {
      type: String,
      default: "Stimulus"
    }
  }
}

Inferred-version:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static values = {
    name: "Stimulus"
  }
}

Add nodes/locations to value definition parts

Ideally we have a node/location for each value in the value definition, while also having the node/location for the property-key and property-value.

Currently we get the range for the whole value of everything after static values =

Support controllers imported from npm packages

An example might be something like tailwindcss-stimulus-components, where you import and register controllers like:

import { Dropdown, Modal } from "tailwindcss-stimulus-components"

application.register('dropdown', Dropdown)
application.register('modal', Modal)

We might be able to statically analyze all Application.register("name", Controller) calls in the app to detect those controllers this way.

Support for `stimulus-use`

Similar to #12, it makes sense that the Stimulus Parser understands the mixins Stimulus Use provides. Some of the mixins Stimulus Use provides enhance the controller implicitly with properties and callback methods.

It would be neat to know that these methods and properties exist on a controller and where they come from. Kind of like a "This property is provided by Stimulus Use" message.

Support inferred Value Definition types

Currently this doesn't get parsed as a valid ValueDefinition, this is valid Stimulus though.

      import { Controller } from "@hotwired/stimulus";

      export default class extends Controller {
        static values = {
          string: "Number"
        }
      }

Add `hasExplicitDefaultValue` to `ValueDefinition`

We should add a hasExplicitDefaultValue property to the ValueDefinition that works according to the use-cases shown below:

static values = {
  name: String
}
name: {
  type: "String",
  defaultValue: "",
  hasExplicitDefaultValue: false,
  kind: "shorthand",
  keyLoc: { ... },
  valueLoc: { ... },
}

static values = {
  name: { type: String },
}
name: {
  type: "String",
  defaultValue: "",
  hasExplicitDefaultValue: false,
  kind: "explicit",
  keyLoc: { ... },
  valueLoc: { ... },
}

static values = {
  name: { type: String, default: "Bob" },
}
name: {
  type: "String",
  defaultValue: "Bob",
  hasExplicitDefaultValue: true,
  kind: "explicit",
  keyLoc: { ... },
  valueLoc: { ... },
}

static values = {
  name: "Bob"
}
name: {
  type: "String",
  defaultValue: "Bob",
  hasExplicitDefaultValue: true,
  kind: "inferred",
  keyLoc: { ... },
  valueLoc: { ... },
}

@Value(String) nameValue!: string;
name: {
  type: "String",
  defaultValue: "",
  hasExplicitDefaultValue: false,
  kind: "decorator",
  keyLoc: { ... },
  valueLoc: { ... },
}

@Value(String) nameValue! = "Bob"
name: {
  type: "String",
  defaultValue: "Bob",
  hasExplicitDefaultValue: true,
  kind: "decorator",
  keyLoc: { ... },
  valueLoc: { ... },
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.