hotwired / stimulus-rails Goto Github PK
View Code? Open in Web Editor NEWUse Stimulus in your Ruby on Rails app
Home Page: https://stimulus.hotwired.dev
License: MIT License
Use Stimulus in your Ruby on Rails app
Home Page: https://stimulus.hotwired.dev
License: MIT License
Hello,
New loader module stimulus-loading.js
, when used in default eager loading mode, silently fails. My setup shows only hello-controller on the list of app assets in dev console. out of 3 controller files, 2 of them are with .erb
. When switched to lazy loading, produces warning
Failed to autoload controller: output
Error: Unable to resolve specifier 'controllers/output_controller' from http://localhost:3003/assets/stimulus-loading-685d40a0b68f785d3cdbab1c0f3575320497
My output_controller.jr.erb
is using erb code and worked fine before updating to new loader in 0.7.1
importmap.rb
:
pin_all_from "app/javascript/controllers", under: "controllers"
sprockets manifest.js
:
//= link_tree ../../javascript .js
I am not sure if this is a bug or something wrong in my app setup.
If I use rails stimulus:manifest:update
task generated imports are using .erb
in the import names, which is not working. But after correcting import names to end with .js
they work.
From a Rails controller I use Turbo Streams to replace a Form element. The form is connected to a StimulusJS controller (input controller) and it was my understanding that the controller should disconnect and reconnect as the DOM element is replaced.
However, it doesn't appear to detect any DOM change and consequently does not connect the controller again after the Form element is replaced. My understanding is StimulusJS uses the DOM MutationObserver API to detect the DOM changes.
Why is it not picking up the DOM change when I use Turbo Streams?
Form element:
<%= form_with model: @game, method: :patch, html: { autocomplete: "off" }, data: { controller: "input" }, id: dom_id(@game), class: "grid justify-center justify-items-center grid-cols-5 gap-y-2 gap-x-2 max-w-md mx-auto mt-2" do |form| %>
<%= form.fields_for :guesses, @game.guesses.order(:created_at) do |ff| %>
<% if ff.object.value != '' %>
<div id="input_<%= dom_id(ff.object)%>" class="w-16 h-16 ">
<%= tag.div :value, class: ["flex justify-center items-center text-center uppercase text-3xl text-white rounded focus:border-cyan-400 w-full h-full", "bg-green-400": ff.object.result == 'match', "bg-orange-400": ff.object.result == 'occurs', "bg-gray-400": ff.object.result == 'miss' ] do %>
<%= ff.object.value %>
<% end %>
</div>
<% else %>
<div id="input_<%= dom_id(ff.object)%>" class="w-16 h-16 ">
<%= ff.text_field :value, class: ["text-center uppercase caret-transparent text-3xl text-white bg-slate-800 border rounded focus:border-cyan-400 w-full h-full", "bg-green-400": ff.object.result == 'match', "bg-orange-400": ff.object.result == 'occurs', "bg-gray-400": ff.object.result == 'miss' ], data: {action: '', input_target: 'inputBox'}, maxlength: "1", autofocus: "true" %>
</div>
<% end %>
<% end %>
<%= form.submit "Submit", class: "hidden" %>
<% end %>
Stimulus Controller:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["inputBox"]
connect() {
this.index = 0
this.focusInput()
}
focusInput() {
console.log("focusInput called")
}
}
Simple install on new Rails 6.1 app. As can be seen on page load, connect occurs twice every time. Running Ruby 3.0.0 and Rails 6.1.1 and latest stimulus-rails gem.
Application.js
import "../stylesheets/application";
import $ from 'jquery';
global.$ = jQuery;
import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
import "controllers"
import { stripeElements } from "./subscriptions";
global.stripeElements = stripeElements;
global.toastr = require("toastr")
Rails.start()
Turbolinks.start()
ActiveStorage.start()
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function(tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
var popoverList = popoverTriggerList.map(function(popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl)
Index.html.haml
.container
%h1 Home#index
%p Find me in app/views/home/index.html.haml
%div{"data-controller" => "home"}
%input{"data-hello-target" => "name", :type => "text"}/
%button{"data-action" => "click->hello#greet"}
Greet
%span{"data-hello-target" => "output"}
Package.js
{
"name": "subscription",
"private": true,
"dependencies": {
"@rails/actioncable": "^6.0.0",
"@rails/activestorage": "^6.0.0",
"@rails/ujs": "^6.0.0",
"@rails/webpacker": "5.2.1",
"jquery": "^3.5.1",
"moment": "^2.29.1",
"stimulus": "^2.0.0",
"toastr": "^2.1.4",
"tooltip": "^1.6.1",
"turbolinks": "^5.2.0"
},
"version": "0.1.0",
"devDependencies": {
"rails-erb-loader": "^5.5.2",
"webpack-dev-server": "^3.11.2"
}
}
Environment.js
const { environment } = require('@rails/webpacker')
const erb = require('./loaders/erb')
const webpack = require('webpack');
environment.plugins.append("Provide",
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
Rails: ['@rails/ujs']
}))
environment.loaders.prepend('erb', erb)
module.exports = environment
I'm using Stimulus in Rails 6 via the Asset Pipeline and want to use either momentjs or dayjs library within my controller. How do I import those?
I've tried saving the dayjs.min.js file to vendors/assets/javascripts/dayjs/dayjs.min.js, but do I need to add anything to app/assets/config/manifest.js? or maybe app/assets/javascripts/importmap.json.erb
manifest.js looks like this:
//= link_tree ../javascripts
//= link_tree ../images
//= link_directory ../javascripts .js
importmap.json.erb looks like this:
{
"imports": {
"turbo": "<%= asset_path "turbo" %>",
<%= importmap_list_with_stimulus_from "app/assets/javascripts/controllers", "app/assets/javascripts/libraries" %>
}
}
This is the bit where I'm stuck. I get the same error:
Failed to autoload controller: timepicker
Error: Unable to resolve specifier 'dayjs' from
http://my.127.0.0.1.xip.io/assets/controllers/timepicker_controller-b2e267b01d1aad58218fa00a049e456870db49eda347728b16443be29c59682f.js
I think then once the library is "available", I should be import it to be available in my Stimulus controller with ๐ :
import { Controller } from "stimulus"
+ import dayjs from 'dayjs'
export default class extends Controller {
...
If I try the following with importmap.json.erb:
{
"imports": {
"turbo": "<%= asset_path "turbo" %>",
+ "dayjs": "<%= asset_path "dayjs/dayjs.min.js" %>",
<%= importmap_list_with_stimulus_from "app/assets/javascripts/controllers", "app/assets/javascripts/libraries" %>
}
}
The error changes to the following when I reload the page:
Failed to autoload controller: timepicker SyntaxError: import not found: default
There are a number of outstanding issues when using this gem with engines. I think it would be helpful to try to collect all the issues so that it's clear what needs fixing, and what's being fixed, and what already works.
importmap_list_from
helper assumes relative paths are relative to Rails.root
, as opposed to the root of an engine (#39 partially fixes this by allowing absolute paths, but it shouldn't be necessary to use absolute paths in the first place). #50 attempts to fix this by introducing new Sprockets directives.app/javascripts/importmap.json
. Because of this, if multiple engines or an engine and an app provide an import map, one will overwrite the other(s). #41 allows import maps to be included from custom locationsI've noticed that when I load a page with the autocomplete controller I get an error in the console:
Failed to autoload controller: autocomplete Error: Unable to resolve specifier 'controllers/autocomplete_controller' imported from http://localhost:3000/assets/stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js
throwUnresolved es-module-shims.js:394
_resolve es-module-shims.js:350
importShim es-module-shims.js:384
loadController stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:70
lazyLoadExistingControllers stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:39
lazyLoadExistingControllers stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:39
lazyLoadControllersFrom stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:34
<anonymous> index-26693b7cb3885f2275520ffa4aafbe6ea299e699.js:6
I have a pretty basic config:
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "application", preload: true
pin "stimulus-autocomplete", to: "https://ga.jspm.io/npm:[email protected]/src/autocomplete.js"
pin_all_from "app/javascript/controllers", under: "controllers"
pin_all_from "app/javascript/lib", under: "lib"
import { Application } from "@hotwired/stimulus"
import { Autocomplete } from "stimulus-autocomplete"
const application = Application.start()
application.register("autocomplete", Autocomplete)
application.debug = false
window.Stimulus = application
export { application }
import { application } from "controllers/application"
import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
lazyLoadControllersFrom("controllers", application)
As soon as I switch to the eager loading the error goes away
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)
In both cases the autocomplete is working, but it's just noisy to get that error in the console. Should manually registered controllers be excluded by the lazy loading under the controllers
path?
I've also tried to remove the preload
from pin "application", preload: true
since application includes a controller, but nothing changed
Hi ๐
I noticed that when I receive new HTML content in a turbo-frame element, my stimulus connect()
method wasn't being called for a controller that wasn't previously referenced in the page. When I refreshed the page, everything worked fine. After doing a bit of investigation, I believe it's due to the autoloader only listening to the turbo:load
event.
I'm happy to open a PR to fix this, but I'm not sure what the best approach is. On the turbo events page, there doesn't seem to be an event that is triggered after turbo-frame content is loaded into the DOM.
A thought I had for how to solve this is to use a mutation observer -- A cool side effect of that route is that if you just edited the DOM and added a data-controller="mycontroller"
element, it should be picked up & autoloaded as well. Not sure if there's edge cases there that I'm missing though (admittedly, I haven't yet used the MutationObserver API ๐
). Also, doesn't stimulus already have a mutation observer on the entire document tree? Would it make more sense to just hook in there?
Has anyone else ran into this? In the meantime, I can workaround by disabling autoloading ๐
Thanks!
... things like CoffeeScript, etc.
The following works for me:
module Stimulus::ImportmapHelper
def find_javascript_files_in_tree(path)
exts = {'.js' => '.js', '.jsm' => '.jsm'}.merge(
Sprockets.mime_exts.map {|key, value|
next unless Sprockets.transformers[value]["application/javascript"]
[key, '.js']
}.compact.to_h)
Dir[path.join('**/*')].map {|file|
file_ext, web_ext = Sprockets::PathUtils.match_path_extname(file, exts)
next unless file_ext
next unless File.file? file
Pathname.new(file.chomp(file_ext) + web_ext)
}.compact
end
end
See https://www.ruby2js.com/examples/rails/stimulus_sprockets for a complete walkthrough.
Firstly, thanks for this library.. it feels like the future! It's also been a great intro to ESM and how that works.
We're running into an issue using this together with a Rails engine, because the importmap_helper.rb
file is hardcoded to look in the Rails.root
folder only, when resolving the paths for stimulus controllers.
def importmap_list_from(*paths)
Array(paths).flat_map do |path|
if (absolute_path = Rails.root.join(path)).exist?
find_javascript_files_in_tree(absolute_path).collect do |filename|
module_filename = filename.relative_path_from(absolute_path)
module_name = importmap_module_name_from(module_filename)
module_path = asset_path(absolute_path.basename.join(module_filename))
%("#{module_name}": "#{module_path}")
end
end
end.compact.join(",\n")
end
We can work around this by manually adding our engine's stimulus controllers to the importmap.json file like so:
{
"imports": {
"turbo": "<%= asset_path "turbo" %>",
<%= importmap_list_with_stimulus_from "app/assets/javascripts/controllers", "app/assets/javascripts/libraries" %>,
"slideshow_controller": "<%= asset_path "slideshow_controller" %>"
}
}
where slideshow_controller
resides under the engine.
It would be great if we could specify which root paths to use in this helper, so we can include AppKit::Engine.root
in the helper path, for example and have it work automatically.
Would this be a welcome option if we gave it a go for a PR? A bit unsure how best to do it, while also keeping the interface simple like it is now: importmap_list_from(*paths)
so open to ideas and suggestions.
First off, thank you all for such a lovely framework. I've been using it in Rails projects and it's been an absolute joy.
Something that I came across during development the other day was a naming convention-related misunderstanding, where I'd be trying to reference a controller with camelCase in a data-action
and nothing fired. The way I fixed this, of course, was to use dash-case in the data-action
, but it made me wonder if there's merit to letting Stimulus Rails recognize controllers by the camelCase equivalent as well, given that's a standard convention in JavaScript.
For example. if we have a controller declared as:
// app/javascript/controllers/multi_word_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
logHello() {
console.log("Hello!")
}
}
And we try to use it in a view like:
<div data-controller="multi-word">
<button data-action="click->multi-word#logHello>Log hello</button>
</div>
This will correctly log "Hello!"
in the console when the button is clicked. However, if the button element is declared as:
<button data-action="click->multiWord#logHello">Log hello</button>
The logHello
method will not fire. Intuitively, I want to write the action declaration the second way, where I'm using camelCase for both the controller name as well as the method name.
Would love to hear a maintainer's take on this!
I get this trying to install Stimulus using the new esbuild-rails
(which I guess has just been replaced by jsbundling-rails
, but I think the issue holds
Uncaught TypeError: __require.context is not a function
, it's from the controllers/index.js
line
const context = require.context("controllers", true, /_controller\.(js|ts)$/)
I'm not sure whether this is a case where the install is giving me the wrong thing (and I should be using the node install and importing all my files one-by-one) or whether there's a way to make the require work.
Thanks
I looked through the manifest.rb and noted that there is only a single flow of logic for the creation of the manifest for controllers. I actually had 2 separate questions here for this.
I have what are basically 3 sections of my application, the homepage, the app UI itself for a logged in user, and then the admin backend. Ideally, in my mind, it would make sense to keep the controllers for these separated out and only load what is necessary for each, so I've been keeping them logically separated within the controllers
folder:
controllers/admin/*_controller.js
controllers/app/*_controller.js
controllers/homepage/*_controller.js
Just given how stimulus-rails
and the manifest works, is this a wrong approach? Is the tradeoff of separation of the JS not enough of a benefit versus just loading all of the controllers at once?
If the answer to the above is that there is a benefit, are there any plans (or aversion to a PR) for allowing for the manifest update task to know, either via an argument or configuration, that certain folder(s) should be delegated out to their own root controller files?
A quick 30s brainstorming session gave me the rough idea of something such as an optional file within the config
folder, stimulus.yml
with a format such as:
admin:
- admin
- foo
app:
homepage:
Such that when rails stimulus:manifest:update
is run, it would check for the configuration file. If it doesn't exist, it would work as normal, but if it does exist it would work such that (in the example of the above) it would generate the following:
admin.js
// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName
import { application } from "./application"
import Admin__BarController from "./admin/some_controller.js"
application.register("admin--bar", Admin__BarController);
import Foo__BazController from "./foo/bar_controller.js"
application.register("foo--bar", Foo__BarController);
app.js
// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName
import { application } from "./application"
import App__FeatureController from "./app/feature_controller.js"
application.register("app--feature", App__FeatureController);
homepage.js
// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName
import { application } from "./application"
import Homepage__FeatureController from "./homepage/feature_controller.js"
application.register("homepage--feature", Homepage__FeatureController);
index.js
// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName
import { application } from "./application"
import SharedFeatureController from "./shared_feature_controller.js"
application.register("shared-feature", SharedFeatureController);
Presumably the stimulus.yml
parsing would need to be intelligent enough to raise an error in the case that application
or index
were passed since those are reserved, but that wouldn't be terribly difficult.
My stimulus controller is not loaded when it is inside an updated turbo frame, why please?
Since work is currently being done on the autoloader (#3, #11) I thought I'd ask - any interest in letting autoloader errors just happen?
The autoloader transforms import errors into a "Failed to autoload controller: ..." message. While writing a stimulus controller, I made a SyntaxError
by importing a wrong module name, and had to stick a debugger into the catch statement to dig it out.
IMHO if something fails to load, swallowing the error a) confuses development, and b) will almost certainly break something downstream anyways unless your controller was entirely cruft.
At the minimum, it should console.log the error, no?
I have a new project configured with webpacker and no sprockets. Stimulus rails fails to load as it is looking for a configuration option that is not available.
Whenever a controller fails to register (for instance if a targetNameConnected()
method throws an error), stimulus will catch the error and display a console message. However that means we can never be alerted of that error, because it doesn't bubble the error to application.handleError
, which I believe is the hook point to setup exception monitoring (appsignal, etc.)
Is it by design, or are you open to a fix?
The instructions in the README did not work for Rails 6.1 until I ran this:
rails webpacker:install:stimulus
You may want to update.
After adding the gem hotwire-rails
to my Gemfile and running bundle exec rails hotwire:install
, I get the following output containing an error (Errno::ENOENT: No such file or directory - bin/bundle
):
Copying Stimulus JavaScript
create app/assets/javascripts
create app/assets/javascripts/controllers/hello_controller.js
create app/assets/javascripts/controllers/index.js
create app/assets/javascripts/importmap.json.erb
create app/assets/javascripts/libraries
create app/assets/javascripts/libraries/.keep
Add app/assets/javascripts to asset pipeline manifest
append app/assets/config/manifest.js
Add Stimulus include tags in application layout
insert app/views/layouts/application.html.erb
Turn off development debug mode
gsub config/environments/development.rb
Turn off rack-mini-profiler
gsub Gemfile
run bin/bundle from "."
rails aborted!
Errno::ENOENT: No such file or directory - bin/bundle
/app/bin/rails:5:in<top (required)>' /app/bin/spring:10:in
require'
/app/bin/spring:10:inblock in <top (required)>' /app/bin/spring:7:in
<top (required)>'
Tasks: TOP => app:template
(See full trace by running task with --trace)
Yield head in application layout for cache helper
insert app/views/layouts/application.html.erb
Add Turbo include tags in application layout
insert app/views/layouts/application.html.erb
Add Turbo to importmap
insert app/assets/javascripts/importmap.json.erb
Enable redis in bundle
gsub Gemfile
Switch development cable to use redis
gsub config/cable.yml
Turbo successfully installed โก๏ธ
rails new app123 --skip-javascript
and add hotwire-rails
gem to the Gemfile.bundle && bundle exec rails hotwire:install
Running bundle binstubs bundler
creates the missing bin/bundle
in my project. I guess the rake task stimulus:install
shouldn't assume that bin/bundle
is present. This is the line which is causing the issue:
Using popper.js, stimulus.js, rails 6, hotwire and tailwind.
Failed to autoload controller: popper Error: Unable to resolve specifier '@popperjs/core' from http://0.0.0.0:3000/assets/controllers/popper_controller-9551ce655873804eb01eade8d7ef77bc460af5cf2da4ebeaa24dbe05675c13e0.js at throwUnresolved (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:472:11) at resolve (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:468:99) at es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:427:26 at Array.map (<anonymous>) at es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:426:39 at async loadAll (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:237:5) at async topLevelLoad (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:256:5)
popper_controler.js:
`import { Controller } from "stimulus";
import { createPopper } from "@popperjs/core";
export default class extends Controller {
static get targets() { return [ "element", "tooltip"] }
static values = {
placement: { type: String, default: "top" },
offset: { type: Array, default: [0, 8] },
};
connect() {
// Create a new Popper instance
this.popperInstance = createPopper(this.elementTarget, this.tooltipTarget, {
placement: this.placementValue,
modifiers: [
{
name: "offset",
options: {
offset: this.offsetValue,
},
},
],
});
}
show(event) {
this.tooltipTarget.setAttribute("data-show", "");
// We need to tell Popper to update the tooltip position
// after we show the tooltip, otherwise it will be incorrect
this.popperInstance.update();
}
hide(event) {
this.tooltipTarget.removeAttribute("data-show");
}
// Destroy the Popper instance
disconnect() {
if (this.popperInstance) {
this.popperInstance.destroy();
}
}
}`
app/javascript/packs/application.js:
`// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
import "@hotwired/turbo-rails"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
import "../stylesheets/application.scss"
import "../stylesheets/popper.scss"
// require("../stylesheets/application.scss")
ActiveStorage.start()
require("trix")
require("@rails/actiontext")
`
spp/javascript/stylesheets/application.scss:
`@tailwind base;
@tailwind components;
@tailwind utilities;
@import "../stylesheets/actiontext.scss";
/* block out first/last segment of background HR */
section.order-workflow ol li {
position: relative;
flex: 1;
> * {
position: relative;
}
// blockers for left/right sides
&:first-child::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 50%;
background: white;
}
&:last-child::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 50%;
right: 0;
background: white;
}
}
}
`
When turning the asset digests off (https://guides.rubyonrails.org/asset_pipeline.html#turning-digests-off) in the development environment by setting config.assets.digest
to false
in config/environments/development.rb
the file assets/stimulus-loading.js
will not load and you will get an HTTP 404 for that file:
Changing config/importmap.rb
to pin to stimulus-loading
instead of stimulus-loading.js
will make it work in the environments where the asset digests are turned off, the file will then however not be included in the production assets when precompiling them.
I have created a new Rails app where you can reproduce this issue here: https://github.com/mrkcor/stimulus-loading-issue
For anyone looking for a workaround for this issue: you can put a copy of the file stimulus-loading.js
in vendor/javascript
and pin the file as is indicated in the README.
If I'm using importmap-rails
and make a mistake while importing a module into a Stimulus controller, the error message related to this is logged with console.debug
here:
// app/assets/javascripts/stimulus-loading.js
import(path)
.then(module => registerController(name, module, application))
.catch(error => console.debug(`Failed to register controller: ${name} (${path})`, error))
This will only appear if the browser's JavaScript console is in Verbose or Debug mode. By default, this is off in Chrome. When I was using Stimulus with Webpacker, this kind of error was logged at a higher level. Would it be possible to make these critical errors more visible so they could be spotted easily in any environment?
Other areas in Stimulus use console.log
for errors. And code in importmap-rails
and turbo-rails
use console.warn
and console.error
for displaying caught exceptions. I think console.error
would make sense when a controller completely fails to load.
See this comment for more on how to recreate the problem.
The changes in #65 included an update to the hello_controller
created by the install script to reference @hotwired/stimulus
instead of stimulus
With this change, Webpacker users will now see the following error when they attempt to compile:
ERROR in ./app/javascript/controllers/hello_controller.js
Module not found: Error: Can't resolve '@hotwired/stimulus' in '/code/new-app/app/javascript/controllers'
resolve '@hotwired/stimulus' in '/code/new-app/app/javascript/controllers'
Steps to reproduce:
rails new my-app
bundle add stimulus-rails
rails stimulus:install
bin/webpack-dev-server
A fix for this might be using gsub_file
after the controllers directory is copied in the Webpacker install script to replace from "@hotwired/stimulus"
with from "stimulus"
which I'm happy to put in a PR for if that's an acceptable path to a resolution.
With a basic Rails 7.0.0 app, running ./bin/rails stimulus:manifest:update
will create a app/javascript/controllers/index.js
file that doesn't seem to work in production, at least not for some people.
Changing this line:
import { application } from "./application"
to:
import { application } from "controllers/application"
And change each controller import from:
import NameController from "./name_controller.js"
to:
import NameController from "controllers/name_controller"
Seems to fix the problem in production.
rails g stimulus hello_world
According to documentation, "when an identifier is composed of more than one word, write the words in kebab-case (i.e., by using dashes: date-picker, list-item)." Therefore, the generated help text in the controller should read:
// Connects to data-controller="hello-world"
Auto-generated help text in the controller incorrectly reads:
// Connects to data-controller="hello_world"
Using this format data-controller="hello_world"
in your view results in the following error
Warning connecting controller "hello_world"
Element references undefined controller "hello_world"
Generated comment doesn't compensate for underscored controller filenames, which are acceptable according to the documentation. "In filenames, separate multiple words using either underscores or dashes (snake_case or kebab-case: controllers/date_picker_controller.js, controllers/list-item-controller.js)."
Rails version: Rails 7.0.0.alpha2
Ruby version: ruby 3.0.1p64
Stimulus-rails version: 0.5.4
Stimulus version: 3.0.0-beta.2
Just tried installing on a new Rails 6.1 codebase. Running ./bin/rails stimulus:install
didn't install the 'controllers' directory or change application.js. Rolling back to 0.4.2 got it all working fine.
I love the autoloader in conjunction with native ESM modules, but I'm running into a race condition that I suspect others will bump into.
In my project I have a canvas that is controlled by a stimulus controller - it allows you to draw on it when clicked.
When I run my capybara system tests, it's not difficult to have the tests fail because the stimulus controller attached to the canvas hasn't yet been loaded. Of course, this problem is pronounced in an automated testing scenario, but given different network conditions, it's not unreasonable to think it could pop up for regular usage situations as well.
I previously solved this problem by eager loading all my stimulus controllers, and suspect that's what I'll end up doing to work around this in the short-term. I'm curious what other thoughts there may be around how to handle this.
In this case, if there were some way to show a message/loading indicator on the canvas, that could help prevent issues of interacting before the stimulus controller had loaded, but I feel like that's not going to be generic enough to apply to every situation. My other thought is maybe to have the autoloader dispatch an event when a controller loads that you could listen to, but I don't love how manual that'd be.
For anyone else that's struggling with this, here's my code that allows you to eager load using native ESM:
<%= javascript_include_tag("stimulus/libraries/es-module-shims", type: "module") %>
<%= tag.script(type: "importmap-shim", src: asset_path("importmap.json")) %>
<%= javascript_include_tag("turbo", type: "module-shim") %>
<%= javascript_include_tag("stimulus/libraries/stimulus", type: "module-shim") %>
<script type="module-shim" type=text/javascript>
import { Application } from 'stimulus'
const application = Application.start()
<% controllers = Rails.root.join('app/assets/javascripts/controllers').children.map { |filename| filename.basename.to_s.remove(filename.extname).split('_controller').first } %>
const controllerNames = <%= controllers.to_json.html_safe %>
controllerNames.forEach((controllerName) => {
let controllerFilename = `${controllerName}_controller`
import(controllerFilename).then((controllerModule) => {
application.register(controllerName.replace('_', '-'), controllerModule.default)
}).catch(error => console.log(`Failed to autoload controller: ${controllerName}`, error))
})
</script>
Of course, you can always just use webpacker and bundle it all together for an easy eager-loading solution too.
Using the ./bin/rails stimulus:manifest:update
command, the index.js
file is automatically updated when all stimulus controllers. This include the application import at the top of the file:
import { application } from "./application"
import ModalController from "./modal_controller"
application.register("modal", ModalController)
When stimulus is installed through the @hotwired/stimulus gem, the auto-generated import lines should be:
import { Application } from "@hotwired/stimulus"
const application = Application.start()
import ModalController from "./modal_controller"
application.register("modal", ModalController)
When running the installer with with rails stimulus:install
on an existing project, it often fails to corretly update files to add the necessary lines of code, showing this error message: File unchanged! The supplied flag value not found!
.
I think it would be helpful to add a section that explains the manual install to the readme, so when somebody installs it and sees that it failed to update a file correctly, they can just do it manually. In addition, we could add what it was supposed to do to the error message, something like:
File unchanged! The supplied flag value not found! app/javascript/application.js
Wanted to add:
import "@hotwired/stimulus-loading"
to app/javascript/application.js, but couldn't parse the file correctly. Please add manually or retry.
Happy to look into this.
I'm fairly new to Rails, so I may just be missing something stupid. Using import maps, how can I write controllers in coffeescript?
I've installed the coffee-rails
gem and set up a controller, but it seems that Stimulus just won't load the controller from this line eagerLoadControllersFrom("controllers", application)
.
I'm certain this is a problem with Coffeescript because manually compiling the Coffeescript and putting the output in a plain JS controller side-by-side with the Coffeescript controller works perfectly; the JS controller hooks up to my DOM element, the Coffeescript controller simply doesn't execute.
Here's my repo demonstrating this (ignore everything in /lib
): https://github.com/Be-brand/game-of-life
From version 0.4.0, stimulus:install
tries to append import "./controllers"
in app/javascript/application.js
instead of app/javascript/packs/application.js
when using the default install of Rails (with webpacker). This also affects hotwire-rails
since it's using the latest version of Stimulus.
Rails version: 6.1.4.1
$ rails new myapp
$ย cd myapp
$ bundle add stimulus-rails
$ rails stimulus:install
Create controllers directory
create app/javascript/controllers
create app/javascript/controllers/index.js
create app/javascript/controllers/hello_controller.js
Import Stimulus controllers
rails aborted!
Thor::Error: The file /..../myapp/app/javascript/application.js does not appear to exist
tabs_controller.js not always auto-loading in Safari but works fine in Chrome.
NOTE: This could be a Safari issue and may have nothing todo with the auto loader.
Rails 6.0.3.4
gem 'hotwire-rails'
Safari Desktop Version 13.1.3 (15609.4.1) Fail but after hard refresh it Pass
Safari Mobile Version 14.0 Fail Still fails even after 'Clear History and Website data'
Chrome Desktop Version 87.0.4280.88 (Official Build) (x86_64) Pass
I am binding to 0.0.0.0 but did start rails in localhost mode and same error.
from the read me it told us too add this
// Eager load all controllers defined in the import map under controllers/**/*_controller
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)
the problem is whenever we use rails stimulus:manifest:update
the settings is removed
After upgrading from 0.4.1 to 0.4.2 I get a bunch of these in the console and, obviously, Stimulus is not working.
The browser points me to then
line in this function:
function registerControllerFromPath(path, under, application) {
const name = path
.replace(`${under}/`, "")
.replace("_controller", "")
.replace(/\//g, "--")
.replace(/_/g, "-")
import(path)
.then(module => application.register(name, module.default))
.catch(error => console.log(`Failed to register controller: ${name} (${path})`, error))
};
Probably an issue in stimulus itselfโฆ
rollup and webpack can perform transpiration of source in languages like coffeescript. The following monkeypatch will enable stimulus:manifest:update
to not only pick up sources that strictly end in .js
, but also controllers that end in .js.whatever
. Perhaps it should be expanded further to support things like .ts
?
module Stimulus::Manifest
def import_and_register_controller(controllers_path, controller_path)
controller_path = controller_path.relative_path_from(controllers_path).to_s
module_path = controller_path.split('.').first
controller_class_name = module_path.camelize.gsub(/::/, "__")
tag_name = module_path.remove(/_controller/).gsub(/_/, "-").gsub(/\//, "--")
<<~JS
import #{controller_class_name} from "./#{controller_path}"
application.register("#{tag_name}", #{controller_class_name})
JS
end
def extract_controllers_from(directory)
(directory.children.select { |e| e.to_s =~ /_controller\.js(\.\w+)?$/ } +
directory.children.select(&:directory?).collect { |d| extract_controllers_from(d) }
).flatten.sort
end
end
See https://www.ruby2js.com/examples/rails/stimulus_rollup and https://www.ruby2js.com/examples/stimulus/ for a complete walkthrough.
P.S. In any case, the regular expression /_controller.js/
should escape the period, thus: /_controller\.js/
.
I believe the following output is perhaps a remnant of previous work?
Add app/javascripts to asset pipeline manifest
append app/assets/config/manifest.js
Everything I see indicates this should be Add app/assets/javascripts
I have created an website where we use stimulus in frontend part. No customer routes has been used there and turbo has been used in the webpage.
When I have just one element with a controller, then everything works as expected and the controller gets connected just once. However, when I have n elements with the same controller, then each element is connected n times.
This can be reproduced with the following controller:
// app/assets/javascripts/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
connect() {
this.element.textContent = "Hello World!"
console.log(this.element)
}
}
It looks like this with two elements with data-controller="hello"
:
Hello there, I have just installed Hotwire gem and everything is working fine with turbo, but I could not manage to make stimulus work.
I am getting: Uncaught Error: Cannot find module 'controllers'
I am using:
Rails 6.0.3.4
Ruby 2.7.1
Stimulus-rails 0.2.2
This is my application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
require("@rails/ujs").start()
require("@rails/activestorage").start()
require("channels")
// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
import "controllers"
require("trix")
require("@rails/actiontext")
I have tried running rails stimulus:install
The error I get is:
Uncaught Error: Cannot find module 'controllers'
at webpackMissingModule
Hello, recently I've noticed that there are some errors thrown in the console from time to time when using Stimulus in a application that I am working on (using Rails 6 & no Webpacker).
I tried to reproduce this problem on a fresh app and it still happens.
Steps to reproduce this problem:
--skip-webpack-install
and --skip-javascript
to get rid of Webpacker,stimulus-rails
gem,hello
controller generated by rails stimulus:install
,Browsers on which I managed to reproduce this problem: Firefox, Firefox Nightly, Firefox Developer Edition, Opera, MS Edge, Chrome for developers.
I have installed stimulus-rails gem on my rails 7 application. When I run the application, I get the following error.
The error is
Uncaught exception: undefined method `application' for Stimulus::Rails:Module
in lib/stimulus/engin.rb, Rails is somehow being interpreted as Stimulus::Rails instead of root class ::Rails.
Does anyone know how to handle this? Help would be greatly appreciated.
The following is the backtrace
=> Booting WEBrick
=> Rails 7.0.2.4 application starting in development http://0.0.0.0:3002
=> Run `bin/rails server --help` for more startup options
Exiting
Uncaught exception: undefined method `application' for Stimulus::Rails:Module
/Users/jae/.rvm/gems/ruby-3.0.4/gems/stimulus-rails-1.0.0/lib/stimulus/engine.rb:4:in `block in <class:Engine>'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:32:in `instance_exec'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:32:in `run'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:61:in `block in run_initializers'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:228:in `block in tsort_each'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:431:in `each_strongly_connected_component_from'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:349:in `block in each_strongly_connected_component'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:347:in `each'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:347:in `call'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:347:in `each_strongly_connected_component'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:226:in `tsort_each'
/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:205:in `tsort_each'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:60:in `run_initializers'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/application.rb:372:in `initialize!'
/Users/jae/src/reiance2/config/environment.rb:5:in `<main>'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/zeitwerk-2.6.0/lib/zeitwerk/kernel.rb:35:in `require'
config.ru:3:in `block in <main>'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:116:in `eval'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:116:in `new_from_string'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:105:in `load_file'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:66:in `parse_file'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/server.rb:349:in `build_app_and_options_from_config'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/server.rb:249:in `app'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/server.rb:422:in `wrapped_app'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:76:in `log_to_stdout'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:36:in `start'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:143:in `block in perform'
<internal:kernel>:90:in `tap'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:134:in `perform'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/command/base.rb:87:in `perform'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/command.rb:48:in `invoke'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands.rb:18:in `<main>'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/Users/jae/src/reiance2/bin/rails:9:in `<top (required)>'
Process finished with exit code 0
@dhh can you release the latest version ๐๐ผ ๐๐ผ
Current release 0.2.2
doesn't work with webpacker since it is missing the definitions
(works from the the master, but... ๐ )
When using the stimulus generator, it always generates a .js regardless if you are using typescript for all your files. It would be great to be able to configure the generator to generate typescript or javascript files.
In trying to use importmap with stimulus `stimulus-loading' and I've run into a challenge with race conditions.
For example, say I have the following code.
// send_controller.js
export default class extends Controller {
connect() {
this.dispatch('connected')
}
}
// receive_controller.js
export default class extends Controller {
catch() {
console.log('catch');
}
}
<div data-controller="receive send"
data-action="send:connected->receive#catch"></div>
connect()
receive#catch
In the case of importmap, sometimes the send controller loads and executes before receive controller loaded. The event is dispatched but nothing happens because the receive controller is not loaded.
I've explored other options:
turbo:load@window->receive#catch
This works for visits, but not not if the content is loaded via turbo-frame
or a FORM turbo-stream
response with lazyLoadControllersFrom
turbo:load@window->receive#catch turbo:frame-load@window->receive#catch
This works for visits and turbo-frame
, but still not for FORM turbo-stream
response with lazyLoadControllersFrom
.
I thought perhaps I could tap into the solution outlined in #57, but the PR is closed.
Is there a solution I'm not aware of that works with visit, turbo-frame and turbo-stream scenarios? Perhaps an event which indicates that all the stimulus controllers have been loaded and ready to execute? Should stimulus hold off on executing javascript until controllers finish loading?
This issue to to (re)report problems autoloading turbo-frame
content (#14), and also to report problems with autoloading when the data-controller
attribute of an element changes. This seems to indicate the mutation observer introduced in #11 isn't actually handling the changes it's supposed to.
turbo-frame
contentWhen turbo-frame
content is loaded, new nodes are added. For a mutation observer to pick up these changes, the observer needs to be able to handle changes to the child list (the option to watch attributes only applies to existing nodes, not added/removed ones). The problem is that the MutationRecord passed to a MutationObserver's callback has different properties depending on the type of mutation. That was the reason for this switch in #3 (there's also a similar example here). Currently though, only attribute mutation events are handled properly in the callback itselfโnot child list mutation events. You can replicate this quickly by adding puma temporarily to the Gemfile and running the dummy app, then adding a node with data-controller="hello"
.
data-controller
attribute changesThe mutation observer callback currently calls this function, passing the element whose data-controller
attribute has changed, but because Element.querySelectorAll()
finds descendants of the element which match the selector, this doesn't return the element with the data-controller
attribute, so nothing gets loaded. To replicate this, do the same as above, but instead change one of the existing nodes' data-controller
attributes to hello
.
By checking the install for the asset pipeline, I've seen the rack-mini-profiler is being commented, but I failed to understand why?
If there is any compatibility issue, can we provide more information so it can be worked out?
Source code: https://github.com/hotwired/stimulus-rails/blob/main/lib/install/stimulus_with_asset_pipeline.rb#L22
Note: I've checked the rack-mini-profiler repo for any info and also did not found anything.
I'm having an issue where my Stimulus controller is not picking up event params for a click action. I'm referencing https://stimulus.hotwired.dev/reference/actions#action-parameters to wire this up. Here's a simple setup I have:
View code:
<%= search_form_for @q, url: "#", html: { data: { controller: "balance form" } } do |f| %>
...
<tr data-action="click->balance#updateTargetRow" data-balance-id-param="2">
...
Stimulus controller code:
// app/javascript/controllers/balance_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
updateTargetRow(event) {
console.log(event.params) //always logs `{}`
...
}
}
Accessing the param via event.currentTarget.dataset.balanceIdParam
gives the expected value of "2" in this example. Is this a known issue or do I have something setup wrong?
Using stimulus 3.0.0 with importmap in rails 7-alpha2, I'm trying:
// application_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
//...
}
// some_controller.js
import { ApplicationController } from "controllers/application_controller";
export default class extends ApplicationController {
// ...
}
FF
Failed to register controller: some (controllers/some_controller) SyntaxError: import not found: ApplicationController stimulus-importmap-autoloader-7366d931317007a1e7e62c8dd8198dbc6d6b438207ff8d8539d06019597bf2f7.js:24:29
Chromium
Failed to register controller: some (controllers/some_controller) SyntaxError: The requested module 'controllers/application_controller' does not provide an export named 'ApplicationController'
Changing the parent controller to:
// application_controller.js
import { Controller } from "@hotwired/stimulus"
export class ApplicationController extends Controller {
//...
}
Failed to register controller: application (controllers/application_controller) TypeError: controllerConstructor is undefined
Not sure is this is a bug on the importmap autoloader or I missing something?
Thanks in advance!
In Rails, Stimulus js response is visible in rails console, i.e. whole HTML page that it sends to client, I would like to turn it off as log is taking more space than it should, how can i achieve it, I couldn't find any solution
I have attached sample of console that is being showing in rails console.
like watching other js file changes, why not auto detected a new js file that created under the app/javascript/controllers
, and then run command like rails stimulus:manifest:update
?
Hi!
#3 Would you think about introduce a change in the ImportmapHelper
to just import files ending in '.js' instead skip only folders and those starting with '.'?
Not sure if all JS libraries end with a .js but so far all stimulus controllers do. And this change will be really helpful when you load folders with a mix of files (not only .js) like in view_components with stimulus controllers.
This is what happen when you include the components folder:
{
"imports": {
"stimulus": "/assets/stimulus/libraries/stimulus-dcca1023dd079a97692e855a18618fc4827389a09a23b23c4ea8916f65d46f09.js",
".keep": "/components/.keep",
"card.html": "/components/card.html.slim",
"card": "/components/card.rb",
"card_controller": "/components/card_controller-6fb9f06f02cc9b20d81deda74ee422fbfe0ca5bba001ea35b3ecb599612caca6.js"
}
}
Thanks!!!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.