Load a Javascript in another Javascript in Play-scala Framework - javascript

How can we load another javascript in js file in Playframe work
This is my conf file
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index
POST /upload controllers.Application.uploadFile
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
I tried a to load js file:
(function(window){
var RECORDER_WORKER_PATH = '/javascripts/recordWorker.js")';
var ENCODER_WORKER_PATH = '/javascripts/mp3Worker.js")';
but i ended up with this in the webconsole:
Error:ScriptFile not found at 'javascripts/recordWorker.js
Error:ScriptFile not found at 'javascripts/mp3Worker.js

Static assets are served under /assets/* per your definition in the routes file.
You should therefore prepend /assets/ to your URIs.
(function(window){
var RECORDER_WORKER_PATH = '/assets/javascripts/recordWorker.js")';
var ENCODER_WORKER_PATH = '/assets/javascripts/mp3Worker.js")';
See Play Asset controller docs for more details.

Related

How do I import variables from a file not located in the same folder [duplicate]

I have the following structure:
-- node_modules
-- websites
---- common
------- config.js
---- testing
------- test.js
Inside config I have some variables set, which are being exported using module.export.
I am trying to retrieve those variables when running node test.js from config.js using the following codes:
var configData = require('./common/config.js')
var configData = require('../common/config.js')
None of them work. What can I do to retrieve the data from the other folder?
var configData = require('./../common/config.js');
./ is testing/
./../ is websites/
./../common/ is websites/common/
./../common/config.js is websites/common/config.js
from test.js:
const configData = require('../common/config');
You can safely omit '.js'.
As documentation say:
File Modules
If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions: .js,
.json, and finally .node.
.js files are interpreted as JavaScript text files, and .json files
are parsed as JSON text files. .node files are interpreted as compiled
addon modules loaded with dlopen.
A required module prefixed with '/' is an absolute path to the file.
For example, require('/home/marco/foo.js') will load the file at
/home/marco/foo.js.
A required module prefixed with './' is relative to the file calling
require(). That is, circle.js must be in the same directory as foo.js
for require('./circle') to find it.
Without a leading '/', './', or '../' to indicate a file, the module
must either be a core module or is loaded from a node_modules folder.
If the given path does not exist, require() will throw an Error with
its code property set to 'MODULE_NOT_FOUND'.
More info about how require() work here.
const cheerio = require('../node_modules/cheerio');
const request = require('../node_modules/request-promise');
const vl = require('../node_modules/validator');
const crypto = require('crypto');
const fs = require('fs');

Rack::Static how to serve an asset file (css, js etc) file containing a fingerprint value in its name so as to cache bust it

In my Rack-based app I want to serve CSS and JS and so I use Rack::Static middleware as shown below:
config.ru
use Rack::Static, urls: ["/css" ], root: "public"
run MyApp
public folder structure:
public
css
application.min.css
As per Rack::Static implementation at https://github.com/rack/rack/blob/2.2.4/lib/rack/static.rb (link refers to code in the version of Rack I am using i.e. 2.2.4) by default Cache-Control header will not be set
in Response.
But if I use following configuration
use Rack::Static, urls: ["/css" ], root: "public",
:header_rules => [
# Cache CSS/JS files, matching given regex in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.1.2.1.css
#
[ /\.(?:[1-9]\.[0-9]\.[0-9])\.(?:css|js)\z/, {'cache-Control' => 'public, max-age=60'} ]
]
Then I can see following header Cache-Control: public, max-age=60 under Response Headers for e.g. in Network tab under Web Developer Tools in Firefox.
Now I want to cache bust that CSS file using fingerprint strategy as explained in following resources I found
https://css-tricks.com/strategies-for-cache-busting-css/#aa-changing-file-name
https://csswizardry.com/2019/03/cache-control-for-civilians/
So in my HTML pages I would have my stylesheet name include the fingerprint version for e.g. like following
<head>
...
...
<link href="/css/application.min.<MY_ASSET_VERSION>.css" rel="stylesheet">
</head>
where say <MY_ASSET_VERSION> is set to 1.0.0.
But I should not have any file by name application.min.1.0.0.css in my public folder. That naming is just done so as to trigger cache bust. So how can I make Rack::Static
to serve the file css/application.min.css when it encounters path /css/application.min.1.0.0.css?
Will I need to implement a middleware which should be put in application's middleware stack after Rack::Static? If yes, can anybody please help me with an example because I have not implemented any middleware.
Or if there is any other standard way for addressing the need at hand, then please suggest that.
Thanks.
Posting below the solution which I implemented using a middleware and which is working for me.
middlewares/custom_middleware/util.rb
module CustomMiddleware
module Util
extend self
EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED = /css|js/
ASSET_FINGER_PRINT_FORMAT_REGEX = /[1-9]\.[0-9]\.[0-9]/
FINGER_PRINTED_ASSET_NAME_MATCHER_REGEX = /\.(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX})\.(?:#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})\z/
ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX = /(.+)\.(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX})\.(#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})\z/
def determine_original_asset_name(fingerprinted_asset_name:)
md = fingerprinted_asset_name.match(ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX)
return fingerprinted_asset_name if md.nil?
arr = md.captures
asset_file_name = arr[0]
asset_file_extension = arr[1]
asset_name = "#{asset_file_name}.#{asset_file_extension}"
asset_name
end
end
end
middlewares/custom_middleware/fingerprinted_asset_name_modifier.rb
require_relative 'util'
module CustomMiddleware
class FingeprintedAssetNameModifier
def initialize(app)
#app = app
end
def call(env)
env_path_info_key = 'PATH_INFO'
orig_path = env[env_path_info_key]
modified_path = Util.determine_original_asset_name(fingerprinted_asset_name: orig_path)
if modified_path != orig_path
env.merge!(env_path_info_key => modified_path)
end
#app.call(env)
end
end
end
config.ru
require_relative "middlewares/custom_middleware/fingerprinted_asset_name_modifier"
use CustomMiddleware::FingeprintedAssetNameModifier
use Rack::Static, urls: ["/css", "/js" ], root: "public",
:header_rules => [
# Cache CSS/JS files in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.css
[ %w(css js), {'cache-control' => 'public, max-age=60'} ]
]
run MyApp
With above solution when following CSS file is included in my page
<head>
...
...
<link href="/css/application.min.1.0.0.css" rel="stylesheet">
</head>
application.min.1.0.0.css file serves my file at public/css/application.min.css and in Response headers Cache-Control: public, max-age=60 is set implying the after 60 seconds if application.min.1.0.0.css is re-requested it will be served from my application and not from browser's cache.
Also within 60 seconds of 1st request to the asset if changing the asset fingerprint in page like following
<link href="/css/application.min.1.0.5.css" rel="stylesheet">
and reloading the page, the asset is served from my application and not from browser's cache.
Hoping that this turns out to be useful to people who may come up with a requirement like in question post.
Thanks.

How to reference assets by URL in JS in rails 7 with importmap and Sprockets?

In an application, I need to instantiate audio files from a JS file (I am using AudioContext API) more or less like this:
playAudio(url) {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
let data = await fetch(url).then(response => response.arrayBuffer());
let buffer = await this.audioContext.decodeAudioData(data)
const source = this.audioContext.createBufferSource()
source.buffer = buffer
source.connect(this.audioContext.destination)
source.start()
}
This JS file is a Stimulus controller loaded in a new Rails 7 application that uses importmap and Sprockets.
In development environment the JS can guess the path as Sprockets will serve the assets with their canonical name (like /assets/audio/file.wav). However, in production, during assets precompilation, Sprockets adds a hash after the file name, and the file will be accessed only with a name like /assets/audio/file-f11ef113f11ef113f113.wav.
This file name cannot be hardcoded as it depends on precompilation (technically I could probably hardcode the path with the hash as the file will not change often, but I do not want to assume anything about this hash).
This file is referenced in the manifest that Sprockets generates during precompilation aside to other assets in the public folder. Using Rails.application.assets_manifest.files I can access the manifest data and do the mapping safely.
Here is the helper I wrote to do it:
def audio_assets_json
audio_assets = Rails.application.assets_manifest.files.select do |_key, file|
file['logical_path'].start_with?('audio/')
end
JSON.pretty_generate(
audio_assets.to_h { |_k, f| [f['logical_path'], asset_url(f['logical_path'])] }
)
end
But I need to access this data from the JS file and as the manifest also has a hash in its file name, my JS cannot simply load it.
My current solution is to include it in my application layout and this works fine:
<script>
window.assets = <%= audio_assets_json %>
window.asset_url = function(path) {
let result = assets[path]
return result ? result : `/assets/${path}`
}
</script>
The issue with this solution is that the hash is written in every single HTML response from the application server, which is not efficient. Also the helper is called at runtime which is also inefficient: this is dynamically generated at runtime whereas this should be done statically during deployment build.
My initial idea was to generate the list in a .js.erb file generated by Sprockets at precompile time. So I renamed controllers/application.js with controllers/application.js.erb and called the helper this way:
<% environment.context_class.instance_eval { include ApplicationHelper } %>
window.assets = <%= audio_assets_json %>
The JS was correctly generated by Sprockets but somehow importmap could not see it and the JS console shows the following error:
Unable to resolve specifier 'controllers/application' from http://localhost:3000/assets/controllers/index-2db729dddcc5b979110e98de4b6720f83f91a123172e87281d5a58410fc43806.js
I tried to add this line in config/initializers/assets.rb:
Sprockets.register_mime_type 'application/javascript', extensions: ['.js.erb']
I tried to add this line in assets/manifest.js:
//= link_tree ../../javascript .js.erb
But none of this helped.
So my question is: How I can reference assets URL from JS using importmap and Sprockets statically?

template node local library import

I have to setup a, most of the time, offline installation of Node-RED and need to use the "Chart.js" Library in a template node. Currently my working approach is to copy the Chart.js dictory to node-red-dashboard/dist/js and import it with <script src= "js/chart.js/dist/Chart.min.js"></script>. But when I want to update the dashboard I need to copy everything again. So it would be nice to have a permanent Solution for this.
I tryed two other approaches until now. For both I installed Chart.js to the .node-red dictory.
First I tryed it like this:
var canvas = document.getElementById('myChart').getContext('2d');
var ChartJs = require('Chart.js');
var chart = new ChartJs(canvas, {... }
in a script tag (... stands for the working chart code that is not edited), but it didn't work aswell as to put
functionGlobalContext: {chartjs:require('Chart.js')} into settings.js and change require('Chart.js') to global.get('chartjs')
Does anyone here has an Idea to solve this properly? Unfortunately the node throws no Error to the console so I don't get an idea whats wrong here.
Thanks in advance for every hint or solution,
manni
When I want to use any 3rd party charting library in my node-red dashboard, I put 2 ui_template nodes into my flow:
under "Template Type" select the "Added to site <head> section" and add the link to the library's url:
<script src="url/to/library.js"></script>
(which in your offline case would be a local url)
use the library's exported objects directly within the template, without using require, such as:
<div id="myChart"></div>
<script>
var canvas = document.getElementById('myChart').getContext('2d');
var chart = new ChartJs(canvas, { ... }
</script>
The trick is to have your local node-red instance serve the ChartJS library through a local url. For that, first add this require path line to the settings.js file, before the exports section:
// The `https` setting requires the `fs` module. Uncomment the following
// to make it available:
var fs = require("fs");
var path = require ("path");
then, uncomment the httpStatic section below that, within the exports:
// When httpAdminRoot is used to move the UI to a different root path, the
// following property can be used to identify a directory of static content
// that should be served at http://localhost:1880/.
httpStatic: path.join(__dirname, 'public'),
(you can use any directory name, in place of public) The __dirname references the node-red server's working directory, usually .node-red under your home directory.
Create this new public directory, copy the ChartJS files under it, and restart node-red. At startup, you should see a line in the console log showing the path to your new static file location:
5 Feb 12:12:23 - [info] Settings file : C:\NODE\node_red_ui\settings.js
5 Feb 12:12:23 - [info] HTTP Static : C:\NODE\node_red_ui\public
5 Feb 12:12:23 - [info] User directory : C:\NODE\node_red_ui
Now you can serve the local file public\scripts\abc.js using the local url
http://localhost:1880/scripts/abc.js
This way, npm updates to the dashboard code will not overwrite your static files.

node.js require from parent folder

I have the following structure:
-- node_modules
-- websites
---- common
------- config.js
---- testing
------- test.js
Inside config I have some variables set, which are being exported using module.export.
I am trying to retrieve those variables when running node test.js from config.js using the following codes:
var configData = require('./common/config.js')
var configData = require('../common/config.js')
None of them work. What can I do to retrieve the data from the other folder?
var configData = require('./../common/config.js');
./ is testing/
./../ is websites/
./../common/ is websites/common/
./../common/config.js is websites/common/config.js
from test.js:
const configData = require('../common/config');
You can safely omit '.js'.
As documentation say:
File Modules
If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions: .js,
.json, and finally .node.
.js files are interpreted as JavaScript text files, and .json files
are parsed as JSON text files. .node files are interpreted as compiled
addon modules loaded with dlopen.
A required module prefixed with '/' is an absolute path to the file.
For example, require('/home/marco/foo.js') will load the file at
/home/marco/foo.js.
A required module prefixed with './' is relative to the file calling
require(). That is, circle.js must be in the same directory as foo.js
for require('./circle') to find it.
Without a leading '/', './', or '../' to indicate a file, the module
must either be a core module or is loaded from a node_modules folder.
If the given path does not exist, require() will throw an Error with
its code property set to 'MODULE_NOT_FOUND'.
More info about how require() work here.
const cheerio = require('../node_modules/cheerio');
const request = require('../node_modules/request-promise');
const vl = require('../node_modules/validator');
const crypto = require('crypto');
const fs = require('fs');

Categories