Git Product home page Git Product logo

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

Watchers

 avatar

stimulus-parser's Issues

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"
        }
      }

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.

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

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 =

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: { ... },
}

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!

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"
  }
}

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'))
}

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 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.

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.

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

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.