I use for dev http protocol and load script like module ->
<script defer type="module" src="App.js"></script>
For some reason for https i need to build it with browserify and esmify to make it work. [not important i already ask Q about this]
In that case i use load script like text/javascript
<script defer src="../../builds/slot.js"></script>
This all works fine if i have app.js in root folder.
Why i can't fix Uncaught TypeError: Failed to resolve module specifier "*******". Relative references must start with either "/", "./", or "../". when my App.js is not in root dir?
For example my import looks like:
import * as blabla from "./../../node_modules/blabla/index.js";
I am using the ruby Gem cssbundling-rails and dart-sass to process Sass in a Rails 7 app (and I am fairly new to Rails).
In the package.json file I define the build:css script:
"build:css": "sass ./app/assets/stylesheets/application.scss ./app/assets/builds/application.css --no-source-map --load-path=. --load-path=./node_modules --load-path=./node_modules/#rpf/sauce --load-path=app/assets/stylesheets/"
This appears to be working; I see the results of several Sass files bundled and processed into one CSS file: app/builds/application.css.
However I cannot see how to add this file to my page. We use Slim so I include the line
= stylesheet_link_tag 'application'
in the app/views/components/_head.html.slim file. That results in this fragment in the final HTML:
<link rel="stylesheet" href="/assets/application-e0cf9d8fcb18bf7f909d8d91a5e78499f82ac29523d475bf3a9ab265d5e2b451.css" />
If I load that file (application-e0cf9d8fcb18bf7f909d8d91a5e78499f82ac29523d475bf3a9ab265d5e2b451.css) into my browser I see it is an empty manifest file which starts
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
and has no files listed below.
What am I missing? How do I load the CSS that was bundled and processed from Sass files into the app/builds/application.css file into my Slim page?
A simple fix is to add
link rel="stylesheet" href="/assets/application.css" type="text/css"
to app/views/components/_head.html.slim instead.
How to use tailwind in a rails engine? According to the documentation supplying a css argument to the Rails generator should work
Rails 7.0.2.2 engine generated using
rails plugin new tailtest --mountable --full -d postgresql --css tailwind
This generates the engine with Postgresql but does nothing with tailwind at all, and following manual installation instructions fail too.
Running, as per documentation, bundle add tailwindcss-rails adds tailwind to the gemfile rather than the engines tailtest.gemspec
So after adding the dependency to the gemspec
spec.add_dependency "tailwindcss-rails", "~> 2.0"
and running bundle install does install the engine however the rest of the manual installation fails
then adding the require to lib/engine.rb
require "tailwindcss-rails"
module Tailtest
class Engine < ::Rails::Engine
isolate_namespace Tailtest
end
end
then running the install process fails
rails tailwindcss:install
Resolving dependencies...
rails aborted!
Don't know how to build task 'tailwindcss:install' (See the list of available tasks with `rails --tasks`)
Did you mean? app:tailwindcss:install
Obviously the app:tailwindcss:install command fails too.
So I am probably missing an initializer of some sort in the engine.rb file but no idea on what it should be.
It is the same idea as How to set up importmap-rails in Rails 7 engine?. We don't need to use the install task. Even if you're able to run it, it's not helpful in the engine (see the end of the answer for explanation).
Also rails plugin new doesn't have a --css option. To see available options: rails plugin new -h.
Update engine's gemspec file:
# my_engine/my_engine.gemspec
spec.add_dependency "tailwindcss-rails"
Update engine.rb:
# my_engine/lib/my_engine/engine.rb
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
# NOTE: add engine manifest to precompile assets in production, if you don't have this yet.
initializer "my-engine.assets" do |app|
app.config.assets.precompile += %w[my_engine_manifest]
end
end
end
Update assets manifest:
# my_engine/app/assets/config/my_engine_manifest.js
//= link_tree ../builds/ .css
Update engine's layout:
# my_engine/app/views/layouts/my_engine/application.html.erb
<!DOCTYPE html>
<html>
<head>
<%#
NOTE: make sure this name doesn't clash with anything in the main app.
think of it as `require` and `$LOAD_PATH`,
but instead it is `stylesheet_link_tag` and `manifest.js`.
%>
<%= stylesheet_link_tag "my_engine", "data-turbo-track": "reload" %>
</head>
<body> <%= yield %> </body>
</html>
bundle show command will give us the path where the gem is installed, so we can copy a few files:
$ bundle show tailwindcss-rails
/home/alex/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/tailwindcss-rails-2.0.8-x86_64-linux
Copy tailwind.config.js file from tailwindcss-rails:
$ cp $(bundle show tailwindcss-rails)/lib/install/tailwind.config.js config/tailwind.config.js
Copy application.tailwind.css file into any directory to fit your setup:
$ cp $(bundle show tailwindcss-rails)/lib/install/application.tailwind.css app/assets/stylesheets/application.tailwind.css
Because tailwindcss-rails uses standalone executable, we don't need node or rails to compile the stylesheets. We just need to get to the executable itself.
Executable is located here https://github.com/rails/tailwindcss-rails/tree/v2.0.8/exe/. Instead of running the build task https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake we can just call the executable directly.
$ $(bundle show tailwindcss-rails)/exe/tailwindcss -i app/assets/stylesheets/application.tailwind.css -o app/assets/builds/my_engine.css -c config/tailwind.config.js --minify
Use -w option to start watch mode.
$ $(bundle show tailwindcss-rails)/exe/tailwindcss -i app/assets/stylesheets/application.tailwind.css -o app/assets/builds/my_engine.css -c config/tailwind.config.js --minify -w
The output file should match the name in stylesheet_link_tag "my_engine".
Now that you have a plain my_engine.css file, do with it what you want. Use it in the layout, require it from the main app application.css. The usual rails asset pipeline rules apply.
If you want to put all that into a task, use Engine.root to get the paths.
# my_engine/lib/tasks/my_engine.rake
task :tailwind_engine_watch do
require "tailwindcss-rails"
# NOTE: tailwindcss-rails is an engine
system "#{Tailwindcss::Engine.root.join("exe/tailwindcss")} \
-i #{MyEngine::Engine.root.join("app/assets/stylesheets/application.tailwind.css")} \
-o #{MyEngine::Engine.root.join("app/assets/builds/my_engine.css")} \
-c #{MyEngine::Engine.root.join("config/tailwind.config.js")} \
--minify -w"
end
From the engine directory:
$ bin/rails app:tailwind_engine_watch
+ /home/alex/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/tailwindcss-rails-2.0.8-x86_64-linux/exe/x86_64-linux/tailwindcss -i /home/alex/code/stackoverflow/my_engine/app/assets/stylesheets/application.tailwind.css -o /home/alex/code/stackoverflow/my_engine/app/assets/builds/my_engine.css -c /home/alex/code/stackoverflow/my_engine/config/tailwind.config.js --minify -w
Rebuilding...
Done in 549ms.
Make your own install task if you have a lot of engines to set up:
desc "Install tailwindcss into our engine"
task :tailwind_engine_install do
require "tailwindcss-rails"
# NOTE: use default app template, which will fail to modify layout, manifest,
# and the last command that compiles the initial `tailwind.css`.
# It will also add `bin/dev` and `Procfile.dev` which we don't need.
# Basically, it's useless in the engine as it is.
template = Tailwindcss::Engine.root.join("lib/install/tailwindcss.rb")
# TODO: better to copy the template from
# https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/install/tailwindcss.rb
# and customize it
# template = MyEngine::Engine.root("lib/install/tailwindcss.rb")
require "rails/generators"
require "rails/generators/rails/app/app_generator"
# NOTE: because the app template uses `Rails.root` it will run the install
# on our engine's dummy app. Just override `Rails.root` with our engine
# root to run install in the engine directory.
Rails.configuration.root = MyEngine::Engine.root
generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root }
generator.apply template
end
Install task reference:
https://github.com/rails/rails/blob/v7.0.2.4/railties/lib/rails/tasks/framework.rake#L8
https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/install.rake
Watch task reference:
https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake#L10
Update How to merge two tailwinds.
Above setup assumes the engine is its own separate thing, like admin backend, it has its own routes, templates, and styles. If an engine functionality is meant to be mixed with the main app, like a view_component collection, then tailwind styles will override each other. In this case isolating engine styles with a prefix could work:
https://tailwindcss.com/docs/configuration#prefix
The reason that tailwind styles don't mix is because most of the selectors have the same specificity and the order is very important.
So here is an example. Main app with an engine, both using tailwind, both compile styles separately, tailwind configs are only watching one file from the engine and one from the main app, only using #tailwind utilities; directive:
Engine template, that we want to use in the main app, should work fine:
<!-- blep/app/views/blep/_partial.html.erb -->
<div class="bg-red-500 sm:bg-blue-500"> red never-blue </div>
But when rendered in the main app it never turns blue. Here is the demonstration set up:
<!-- app/views/home/index.html.erb -->
<%= stylesheet_link_tag "blep", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
<!-- output generated css in the same order as above link tags -->
<% require "open-uri" %>
<b>Engine css</b>
<pre><%= URI.open(asset_url("blep")).read %></pre>
<b>Main app css</b>
<pre><%= URI.open(asset_url("tailwind")).read %></pre>
<div class="bg-red-500"> red </div> <!-- this generates another bg-red-500 -->
<br>
<%= render "blep/partial" %>
And it looks like this:
/* Engine css */
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity))
}
#media (min-width: 640px) {
.sm\:bg-blue-500 {
--tw-bg-opacity: 1;
background-color: rgb(59 130 246 / var(--tw-bg-opacity))
}
}
/* Main app css */
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity))
}
<div class="bg-red-500"> red </div>
<br>
<div class="bg-red-500 sm:bg-blue-500"> red never-blue </div>
^ you can hit run and click "full page". Main app bg-red-500 selector is last so it overrides engines sm:bg-blue-500 selector, media queries don't add to specificity score. It's the same reason you can't override, say, mt-1 with m-2, margin top comes later in the stylesheet. This is why #layer directives are important.
The only way around this is to watch the engine directory when running tailwind in the main app, so that styles are compiled together and in the correct order. Which means you don't really need tailwind in the engine:
module.exports = {
content: [
"./app/**/*",
"/just/type/the/path/to/engine/views",
"/or/see/updated/task/below",
],
}
Other ways I tried, like running 6 tailwind commands for each layer for main app and engine, so that I can put them in order, better but was still out of order a bit and duplicated. Or doing an #import and somehow letting postcss-import know where to look for engine styles (I don't know, I just symlinked it into node_modules to test), but this still required tailwind to watch engine files.
I did some more digging, tailwind cli has a --content option, which will override content from tailwind.config.js. We can use it to setup a new task:
namespace :tailwindcss do
desc "Build your Tailwind CSS + Engine"
task :watch do |_, args|
# NOTE: there have been some updates, there is a whole Commands class now
# lets copy paste and modify. (debug = no --minify)
command = Tailwindcss::Commands.watch_command(debug: true, poll: false)
# --content /path/to/app/**/*,/path/to/engine/**/*
command << "--content"
command << [
Rails.root.join("app/views/home/*"),
Blep::Engine.root.join("app/views/**/*.erb")
].join(",")
p command
system(*command)
end
# same for build, just call `compile_command`
# task :build do |_, args|
# command = Tailwindcss::Commands.compile_command(debug: false)
# ...
end
https://github.com/rails/tailwindcss-rails/blob/v2.0.21/lib/tasks/build.rake#L11
That answer by Alex is really good, i wish i had it when starting out. (But i didn't even have the question to google)
Just want to add two things:
1- a small simplification. I just made a script to run tailwind in the engine
#!/usr/bin/env sh
# Since tailwind does not install into the engine, this will
# watch and recompile during development
# tailwindcss executable must exist (by bundling tailwindcss-rails eg)
tailwindcss -i app/assets/stylesheets/my_engine.tailwind.css \
-o app/assets/stylesheets/my_engine/my_engine.css \
-c config/tailwind.config.js \
-w
2- For usage in an app, that obviously also uses tailwind, i was struggling, since the two generated css's were biting each other and i could not get both styles to work in one page. Always one or the other (app or engine) was not styled right. Until i got the app's tailwind to pick up the engines classes.
Like so:
Add to the app's tailwind.config.js: before the module
const execSync = require('child_process').execSync;
const output = execSync('bundle show my_engine', { encoding: 'utf-8' });
And then inside the content as last line
output.trim() + '/app/**/*.{erb,haml,html,rb}'
Then just include the apps generated tailwind css in the layout, like the installer will. Don't include the engines stylesheet in the layout, or add it to the asset
I'm trying to add TypeScript to an existing VueJS + Rails app. I cloned this demo (https://github.com/gbarillot/rails-vue-demo-app) then followed the instructions from https://github.com/rails/webpacker
$ bundle exec rails webpacker:install:vue
$ bundle exec rails webpacker:install:typescript
I then modified config/webpack/loaders/typescript.js as described here.
Everything seems to compile, but when I go into my "home" view and change the script to typescript:
<script lang="ts">
import Layout from '../shared/layout';
export default {
components: {
Layout
}
}
</script>
I get the following error:
Failed to compile.
/Users/matt/projects/rails-vue-demo-app/app/javascript/packs/components/home/index.vue.ts
[tsl] ERROR in /Users/matt/projects/rails-vue-demo-app/app/javascript/packs/components/home/index.vue.ts(13,20)
TS2307: Cannot find module '../shared/layout'.
Why can I no longer find the layout file when typescript is enabled?
I remember running into a similar problem for presentational components. Try adding an empty export in shared/layout so typescript can pick it up:
<script lang="ts">
export default {}
</script>
What is the best strategy to deploy a Dart Web-ui app manually ?
pub deploy doesn't work for me and I have raised bug report. So am thinking what is the best way to manually deploy.
This is how I started:
1) From project root I compile the webui components (dwc.dart)
2) change directory to web/out then run dart2js
3) copy all .js files into that scripts/js public folder on server
4) copy appname.html to server changing css and script paths to option 3
5) Make sure dart.js is also in the same directory as item 3
this is as far as I got. So what else do I need to do ?
A few questions:
1) Do I manually change the file paths in the generated .js files to point to public folders on server for the files they are referencing and make sure those files are on server also ?
2) Do I need to copy all packages to server also ?
3) Any preferred file structure on server?
Any tips on this really appreciated.
Thanks.
I wrote a Grunt script for it (since I had no time to look up how to properly write code for Grunt, I did not share the code since it's a mess) but I basically do this:
compiling a list of files with dwc to a given out dir
compile it to javascript
clean up all non-deployable files
change some paths inside the HTML to match the server paths (for some reasons, this gets changed by the compilation process)
remove all packages except the ones I really need (JS interopt and browser)
Since I'm only using the JS version, I remove all dart packages. Since the paths inside the HTML files are up to you, you can already use a structure that suits you/your server.
I can provide you with a Grunt script to understand the order of tasks. Practically the order I use is this one:
Create the build directory. I usually use /build/web. I usually create these files (index.html, main.dart, /css and so on into the /web dir). I create the rest of components into /lib directory.
Compile the .dart file that contains the main() function ("main.dart" in my case for simpler projects) file to Javascript and put it into /build/web directory
Copy the other needed files and folders to the /build/web directory. Also, during this process you'll be copying the packages that your project needs. You'll see in the example provided below.
Remove all empty folders from the project
You can create a Grunt task to open the /index.html file in the browser once the building process has ended (I will not provide this example)
The structure of the dart test project:
testApp
- gruntfile.js
- package.js
/lib
/packages
/angular
/web
- index.html
- main.dart
/css
/img
So, the Grunt example script to cover steps from 1 - 4 looks like this (copy it to gruntfile.js):
module.exports = function (grunt) {
grunt.initConfig({
// 1.
// create build web directory
mkdir: {
build: {
options: {
create: ['build/web']
}
}
},
// 2.
// compile dart files
dart2js: {
options: {
// use this to fix a problem into dart2js node module. The module calls dart2js not dart2js.bat.
// this is needed for Windows. So use the path to your dart2js.bat file
"dart2js_bin": "C:/dart/dart-sdk/bin/dart2js.bat"
},
compile: {
files: {'build/web/main.dart.js': 'web/main.dart'}
}
},
// 3.
// copy all needed files, including all needed packages
// except the .dart files.
copy: {
build: {
files: [
{
expand: true,
src: [
'web/!(*.dart)',
'web/css/*.css',
'web/res/*.svg',
'web/packages/angular/**/!(*.dart)',
'web/packages/browser/**/!(*.dart)'
],
dest: 'build'
}
]
}
},
// 4.
// remove empty directories copied using the previous task
cleanempty: {
build: {
options: {
files: false
},
src: ['build/web/packages/**/*']
}
},
});
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
grunt.registerTask('default', [
'mkdir:build',
'dart2js',
'copy:build',
'cleanempty:build'
]);
};
So this is the Grunt script example.
Create a /gruntfile.js file into your project's root directory and copy/paste the script to it.
Create a /package.json file into your project's root directory and copy/paste the following script:
{
"name": "testApp",
"version": "0.0.1",
"description": "SomeDescriptionForTheTestApp",
"main": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "YourName",
"peerDependencies": {
"grunt-cli": "^0.1.13"
},
"devDependencies": {
"grunt": "^0.4.5",
"grunt-cleanempty": "^1.0.3",
"grunt-contrib-copy": "^0.7.0",
"grunt-dart2js": "0.0.5",
"grunt-mkdir": "^0.1.2",
"matchdep": "^0.3.0"
}
}
Open Command Prompt in Windows, Terminal in Linux, navigate to your project's root directory and use this command:
npm install
Wait untill all Grunt modules needed will be downloaded to your local project. Once this is finished, issue this command in Command Prompt or Terminal:
node -e "require('grunt').cli()"
You can use this to initiate Grunt default task without having Grunt installed globally on your system.
Now, to know the exact build structure for your project (including the packages that the project needs), make a build using Pub Build. Then you will be able to instruct Grunt to create the same dir structure.
You can add other tasks (like minification) if you want.
Hope this will help you all to understand the process and get you started with a test app first. Add your comments to make this even better and simplify it even more.