RequireJS Optimizer - still attempting to load files from old dir - w/ Django - javascript

Need some help with the requireJS optimizer.
My setup is as follows:
Django
JS inside the Django static folder
Folder structure
- dist
- copy of /static/ after optimizer (dir)
- django_app01
- django_app02
- django_app ...
- static
- bower_components
- js
What I'm doing:
Running r.js on the static folder.
r.js optmizes and moves the files to the "dist" folder as expected
Change the "static_url" settings in Django to get the static files from the "dist" folder.
Load the page and get a bunch of 404's as RequireJS is still trying to get modules from /static/...
Here's an example of the url's it's trying to fetch:
localhost/static/...
Rather than
localhost/dist/...
Any ideas why I'm getting all of these 404's after I run the optimizer. I expected the r.js optimizer to start to look for all the files in /dist.
build.js:
mainConfigFile : "static/js/require/common.js",
baseUrl: "static",
dir: "dist",
removeCombined: false,
findNestedDependencies: false,
modules: [
{
name: "js/require/common"
},
...
]
common.js:
requirejs.config({
baseUrl: '/static/',
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min',
...
},
'shim': {
'blah': {
'deps': [...],
'exports': 'blah'
},
...
}
})
html:
<head>
<script src="//cdn.jsdelivr.net/requirejs/2.1.14/require.min.js"></script>
<script src="{% static 'js/require/common.js' %}"></script>
</head>
<script>
require(['{% static "js/interviews.js" %}']);
</script>

Judging by what you do show in your question, the runtime configuration you use when you do not optimize your files is the same as the one you use after optimization. That is, you have only one common.js file that sets your runtime for both cases. This file sets a baseUrl of /static/, so that's where RequireJS looks for the files. The optimization process does not override this setting behind the scenes.
For a project of mine, what I did was to put the sources subject to optimization into a directory that is just for the source (static-src). Django does not know anything about this directory. Then either one of two things happen when I run make to build my project:
I make a non-optimized build which copies all the files from static-src to a directory named build/static-build. (There are other subdirectories for other purposes under build.
I make an optimized build which runs r.js. r.js puts its output in build/static-build.
In either case, everything ends up in the same location. STATICFILES_DIRS is set in my settings to grab files from this directory when collectstatic is run. My server is set to serve the /static/ (the value of STATIC_URL) files from the location where collectstatic collected the static files.
This is just an example. You could use something else than make. You could certainly use different directory names. You could perhaps have a build process that is a bit simpler. (I created it when I was very new to Django and never bothered changing it.)

Okay, figured out how to do this without using a watcher, or make, or any other folder copy script madness.
requireJS allows you to set default variables before you load your require.js lib at the top of your HTML.
Changes I made from my previous configuration are:
- No longer define baseUrl in your module definitions (requirejs.config)
- Define baseUrl as a default depending on the environment.
build.js:
mainConfigFile : "static/js/require/common.js",
baseUrl: "static",
dir: "dist",
removeCombined: false,
findNestedDependencies: false,
modules: [
{
name: "js/require/common"
},
...
]
common.js:
requirejs.config({
// baseUrl: '/static/', DO NOT DEFINE baseUrl here anymore
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min',
...
},
'shim': {
'blah': {
'deps': [...],
'exports': 'blah'
},
...
}
})
html:
<head>
<!-- Define the baseUrl depending on environment -->
<script>
var require = {
baseUrl: {% if debug %}'/static/'{% else %}'/dist/'{% endif %}
}
</script>
<script src="//cdn.jsdelivr.net/requirejs/2.1.14/require.min.js"></script>
<script src="{% static 'js/require/common.js' %}"></script>
</head>
<script>
require(['{% static "js/interviews.js" %}']);
</script>

Related

Caching issue js files using requirejs

I keep having issues with the cached js files, which causes the user to not have the latest js and the browser to give js errors on files it can't find that should be included with requirejs.
My requirejs (version 2.3.6) setup is as follows:
<script src="{% static 'main/js/require.js' %}"></script>
<script>
function requireConfig(urlArgs) {
requirejs.config({
baseUrl: '/static/main/js/',
urlArgs: urlArgs,
waitSeconds: 15,
packages: [{
name: 'moment',
location: '//cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0',
main: 'moment'
}],
paths: {
// vendors
'jquery': '//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min',
'jquery_ui': '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min' ,
'text_contrast': './vendors/text_contrast',
},
shim: {
'jquery_ui': {
deps: ['jquery']
},
'text_contrast': {
exports: 'hex',
},
}
});
}
</script>
<script type="text/javascript">
requireConfig("bust=v18");
require(['platform/init'], function (m) {
m.init({
debug: {{ debug|yesno:"true,false" }},
});
});
</script>
I include this before the closing body tag. Whenever the user refreshes the page after clearing his cache, all (new) js files with the bust argument are loaded correctly. The problemen is, I cant force my users to clear its cache everytime I deploy some new js.
Update
If I add +(new Date()).getTime() to the requireConfig function, the files now get the time argument, which seems to work. But on some urls the browser still looks for a js file at the wrong path. For example when I go to an url it loads:
/static/main/js/text_contrast.js?bust=v181560938449189
which gives a 404.
After clearing my cache and a reload the file is found at:
/static/main/js/vendors/text_contrast.js?bust=v181560939213670
This seems to occur on urls I've already loaded in the past with that wrong path pointing to the text_contrast.js file
You can add time stamp to the urlArgs in the requirejs.config object so that url's will not be cached.
One way you can do it is by changing your code from
requireConfig("bust=v18");
to
requireConfig("bust=v18"+(new Date()).getTime());

Issues with require.js and sourcemaps when compiling typescript with grunt-ts

When using grunt-ts and specifying and out file my app runs as expected, but since that option has no support for fast compilation, i tried using a regular compilation where all my .ts files live on the dist folder
There are two issues, first, it will fail to load any imported file, since it will try to look for it without extension. As a quick fix i edited the load fn on the require.js file and all my dependencies load correctly, but then the sourcemaps stopped working, all i get is a blank file on the chrome inspector (and of course i don't want to rely on a dirty hack) .
Please note that i'm not very familiar with requirejs so I'm not quite sure if this is a misconfiguration on my side, a bug, or something that is missing.
My grunt config, related to ts
ts: {
options: {
module: 'amd',
target: 'es5',
experimentalDecorators: true
},
dev: {
src: ['client-app/**/*.ts'],
outDir: "dist",
watch: '.'
}
},
My bootstrap.js which is just the entry point and require.js config file
requirejs.config({
baseUrl: '.',
waitSeconds: 20
});
requirejs(['/init'], function(module) {
module.main()
});
A simplified version of the compiled init file
define(["require", "exports", './section-manager.view'], function (require, exports, section_manager_view_1,) {
"use strict";
function main() {
///
}
exports.main = main;
});
//# sourceMappingURL=init.js.map
And the html
<script src="/js/lib/require.js" data-main="/bootstrap"></script>
Any help is appreciated, thanks
Edit:
Using System.js or #Luis answer solves the loading issue.
The sourcemap issue is solved by using sourceRoot or
inlineSourceMap: true,
inlineSources: true
To the ts options
Don't use an absolute module name. If you do use an absolute name, RequireJS assumes that you do not want any alteration when it generates a path from the module name and will not add the .js extension. You should use a relative path, or you could do this:
requirejs.config({
baseUrl: '/'
});
requirejs(['init'], function(module) {
module.main()
});
By doing this, RequireJS will automatically add the .js extension.

Systemjs builder remove file extension

I have couple of es6 modules. add.js, sub.js and calc.js.
calc.js imports add.js and sub.js.
I am building them with grunt and SystemJS builder. My grunt configuration for this looks like -
systemjs: {
es6: {
options: {
baseURL: "/",
configFile: "config.js",
},
files: [{
"src": ["src/main/calc.js"],
"dest": "dist/calc.js"
}]
}
}
Once the grunt build is done, it creates the resultant calc.js and in that file I can see this :
System.register("src/main/calc.js", [..
..................
I don't want the .js in that name. I want it something like :
System.register("src/main/calc", [..
What should I do? Am I missing some configuration ?
This naming behavior is by design. bundle() creates a bundle for SystemJs to use in the browser, so the names must match the file names that SystemJs will be attempting to load. If you are trying to avoid SystemJs in the browser, buildStatic() is what you are looking for.
There is paths option for the builder.
paths: {
"src/main/calc": "src/main/calc.js"
}
FYI, there is map option. I thought map would be the option for the purpose but it never worked for me.

grunt requirejs ignores paths from my mainConfigFile

Project Intro
My project is a single page storefront. The project has multiple modules, and each module contains a set of controller.js, view.js and model.js files, as well as a template.html file. And uses requirejs to manage dependencies.
Problem Statement
I want to use mainConfigFile to provide paths to reference modules in grunt-requirejs.
Part of my mainConfigFile's require.config is stored in separate file (base.dependency.config), and require.config.paths are pieced together by underscore at runtime.
base.dependency.config
config = {
baseDependencyConfig: {
paths: { ... }
shim: { ... }
}
}
main.js
var dependencies = config.baseDependencyConfig;
var basePaths = config.baseDependencyConfig.paths;
var extensionPaths = {
// extra sets of paths
};
// combine base paths and extension paths at runtime using underscore
var dependencyPaths = _.extend(basePaths, extensionPaths);
dependencies.paths = dependencyPaths;
require.config(dependencies);
// application startup
require(['app', 'eventbus']) {
// code
}
Error
However, grunt requirejs is ignoring mainConfigFile, grunt requirejs tries to find 'app.js' under root, when in fact, 'app' is defined under require.config paths as
'app': 'modules/base/app/base.app.controller'
my gruntFile:
module.exports = function (grunt) {
grunt.initConfig({
// ... other plugin config
requirejs: {
options: {
baseUrl: 'public',
// the paths for the named modules such as 'app' are defined
// in main.js under require.config paths
name: 'main',
include: [
'app',
'cart',
'category'
],
out: 'public/build/app-optimized.js',
mainConfigFile: 'public/main.js',
findNestedDependencies: true,
optimizeCss: 'none',
cssImportIgnore: 'style/style.css, style/mocha.css',
}
}
})
}
my file structure
public
|--modules/
| |--base/
| | |--cart
| | |--category
| | |--category.controller.js
| | |--category.view.js
| | |--category.model.js
| | └-category.template.html
| |
| └--extension/
|
|--style/
|--image/
|--main.js <-- main config file
|--other .js files
mainConfigFile, main.js lives in root, along with a few other application startup js files
main bulk of application files lives inside modules folder
each module folder contains its controller, view and model js file, as well as a template.html file
Edit
the gruntFile worked before, with different mainConfigFile (main.js) setup:
require.config({
paths: {...}
shim: {...}
})
// application startup
require(['app', 'eventbus']) {
// code
}
r.js uses Esprima as Javascript parser to extract the config object from the specified mainConfigFile. It only looks for certain signatures in the code.
Look at
hasRequire(): determine the AST node is a configuration call candidate
findConfig(): calls the above deciding how to extract the config
I've created a patch making it aware of recognizing
requirejs.config(_VariableToExport = { ... });
This is limited and the Javascript parser approach makes it very complicated to make r.js able to extract configurations that were created by functions (like in your code) or so. This patch has not been submitted to the project yet. I'm struggling with bureaucracy yet.
I think the only working workaround so far is
not to use mainConfigFile
exporting the config as NodeJS module
requiring the main.js/config.js in Node (Grunt)
passing the object as value to the config attribute or method
See my comment to this issue for a scribble.
This approach is proven in another, a bit older project I'm working on.
Related to r.js 2.1.11.

Reusing/sharing views & models in different projects with Durandal JS

I'm building multiple applications using Durandal JS. All those applications are located on the same server under the same document root and share some common code. For example they all use the same model & view for login.
How can i reuse/share the login model & view in all those applications without just copy & pasting the files to the projects?
I already tried something with the following folder structure:
ProjectsDir/Project1/app/durandal/..
/models/Shell.js, Main.js, ...
/views/Shell.html, Main.html, ...
/main.js
/main-built.js
ProjectsDir/Project2/app/durandal/..
/models/Shell.js, Main.js, ...
/views/Shell.html, Main.html, ...
/main.js
/main-built.js
ProjectsDir/ProjectsBase/app/models/Login.js
/views/Login.html
This way it would be possible to reference the same login model & view in my ProjectsBase from all other projects by setting the correct route to it in the respective shell.js. This route could look something like this:
router.map([
{
url: 'Login',
moduleId: '../../ProjectsBase/app/models/Login',
name:'Login',
visible: true
},
{
url: 'Main',
moduleId: 'models/Main',
name:'Main',
visible: true
}
]);
This works as expected during debugging but building the production version with the durandal optimizer unfortunately doesn't work.
Actually building does work (it produces the main-built.js just fine) but when i launch the site with the production file referenced i get the following error:
Uncaught Error: undefined missing durandal/../../../MPBase/durandal-app/models/Login
I'd really appreciate any ideas on how I could make the built production file work with the setup I described above.
Of course I'm also open for other ideas on how to make models & views reusable/sharable between multiple projects.
Thanks
With some help from Durandals Google Group I found a solution.
It's not possible to use the provided optimizer.exe but it's quite easy to create a custom r.js config which can handle the setup I described in the question:
First of all I ran the optimizer.exe which created a basic config file (app.build.js) that i used as a starting point.
This config file automatically included all necessary files from the project itself (e.g. Project1).
The only things that are missing in this config file are the references to my shared code (the login files from the ProjectsBase directory). Therefore I added them manually along with a new path.
Custom app.build.js (3 changes highlighted with a comment, the rest is how it was built from the optizimer.exe):
{
"name": "durandal/amd/almond-custom",
"inlineText": true,
"stubModules": [
"durandal/amd/text"
],
"paths": {
"text": "durandal/amd/text",
"projectsbase": "../../ProjectsBase/" // New path to folder with shared files
},
"baseUrl": "ProjectsDir\\Project1\\app",
"mainConfigFile": "ProjectsDir\\Project1\\app\\main.js",
"include": [
"main",
"durandal/app",
"durandal/composition",
"durandal/events",
"durandal/http",
"text!durandal/messageBox.html",
"durandal/messageBox",
"durandal/modalDialog",
"durandal/system",
"durandal/viewEngine",
"durandal/viewLocator",
"durandal/viewModel",
"durandal/viewModelBinder",
"durandal/widget",
"durandal/plugins/router",
"durandal/transitions/entrance",
"projectsbase/app/models/Login", // Include for login model
"models/Main",
"models/Shell",
"text!projectsbase/app/views/Login.html", // Include for login view
"text!views/Main.html",
"text!views/Shell.html"
],
"exclude": [],
"keepBuildDir": true,
"optimize": "uglify2",
"out": "ProjectsDir\\Project1\\app\\main-built.js",
"pragmas": {
"build": true
},
"wrap": true,
"insertRequire": [
"main"
]
}
Now I only had to update my Shell.js to use the correct routes to the Login model & view by also adding a path to requirejs and using it correctly when setting the routes:
Add path at the very beginning of Shell.js:
requirejs.config({
paths: {
'projectsbase': '../../ProjectsBase/'
}
});
Set correct routes in activate method of Shell.js:
router.map([
{ url: 'Login', moduleId: 'projectsbase/app/models/Login', name:'Login', visible: true },
{ url: 'Main', moduleId: 'models/Main', name:'Main', visible: true }
]);
Now i can build my main-built.js which bundles & minifies all relevant files by opening the node js command line, browsing to the directory where the r.js config file is and create the build (the main-built.js) with the following command:
node r.js -o app.build.js
This way everything is included correctly when I'm working with the debug files and it's also working with the build main-built.js which also includes my shared files from the ProjectsBase.

Categories

Resources