I am using the following build file and when I build (r.js -o jsbuild/build.js) all the files in the 'script' folder are minified into the 'productionScripts' folder but they are not combined into the config.js file. Therefore I'm still getting the multiple http requests for all the dependencies.
Is there something wrong with my config or am I completely missing something about requireJS?
({
appDir : "../assets/scripts",
baseUrl : "",
dir : "../assets/productionScripts",
optimize: "uglify",
paths: {
config: 'assets/scripts/config'
},
modules: [
{
name: "config"
}
],
mainConfigFile : "../assets/scripts/config.js"
})
Of course once I post I figure it out. I was mixing concepts. My config was saying minify the 'assets/scripts' folder and that's what it was doing.
I updated the script to just minify the main file. In this case 'assets/scripts/config.js' and that's when it combines dependencies. See appropriate config below. The key is to not use 'dir', 'appDir', and 'modules', this is specific to minifying the folder. Use 'out' to specify where dependencies will be minified and combined.
({
baseUrl : "../assets/scripts",
optimize: "uglify",
name: 'config',
mainConfigFile : "../assets/scripts/config.js",
out: "../assets/productionScripts/config.js"
})
Related
Project Structure
I have a monorepo (using npm workspaces) that contains a directory api (an express API written in typescript). api uses a local package #myapp/server-lib (typescript code).
The directory structure is:
.
├── api/
└── libs/
└── server-lib/
Problem
When I build api using tsc, the build output contains require statements for #myapp/server-lib (the server-lib package). However, when the API is deployed the server can't resolve #myapp/server-lib (since its not meant to be installed from the npm registry).
How can I get tsc to compile #myapp/server-lib removing require statements for #myapp/server-lib in the built code and replacing it with references to the code that was being imported?
The behavior I am looking to achieve is what next-transpile-modules does for Next.js.
I tried to use typescript project references, that did not compile the imported #myapp/server-lib. I also read up on why I didn't encounter this issue in my NextJS front-end (also housed in the same monorepo, relying on a different but very similar local package) and that is how I landed on next-transpile-modules.
Would appreciate any help or tips in general on how to build a typescript project that uses a local package. Thank You!!
UPDATE (12/28/2022)
I solved this by using esbuild to build api into a single out.js file. This includes all dependencies (therefore #myapp/server-lib.
The overall build process now looks like:
npx tsc --noEmit # checks types but does not output files
node build.js # uses esbuild to build the project
Where the build.js script is:
const nativeNodeModulesPlugin = {
name: 'native-node-modules',
setup(build) {
// If a ".node" file is imported within a module in the "file" namespace, resolve
// it to an absolute path and put it into the "node-file" virtual namespace.
build.onResolve({ filter: /\.node$/, namespace: 'file' }, args => ({
path: require.resolve(args.path, { paths: [args.resolveDir] }),
namespace: 'node-file',
}))
// Files in the "node-file" virtual namespace call "require()" on the
// path from esbuild of the ".node" file in the output directory.
build.onLoad({ filter: /.*/, namespace: 'node-file' }, args => ({
contents: `
import path from ${JSON.stringify(args.path)}
try { module.exports = require(path) }
catch {}
`,
}))
// If a ".node" file is imported within a module in the "node-file" namespace, put
// it in the "file" namespace where esbuild's default loading behavior will handle
// it. It is already an absolute path since we resolved it to one above.
build.onResolve({ filter: /\.node$/, namespace: 'node-file' }, args => ({
path: args.path,
namespace: 'file',
}))
// Tell esbuild's default loading behavior to use the "file" loader for
// these ".node" files.
let opts = build.initialOptions
opts.loader = opts.loader || {}
opts.loader['.node'] = 'file'
},
}
require("esbuild").build({
entryPoints: ["./src/server.ts"], // the entrypoint of the server
platform: "node",
target: "node16.0",
outfile: "./build/out.js", // the single file it will bundle everything into
bundle: true,
loader: {".ts": "ts"},
plugins: [nativeNodeModulesPlugin], // addresses native node modules (like fs)
})
.then((res) => console.log(`⚡ Bundled!`))
.catch(() => process.exit(1));
Solved (12/28/2022)
I solved this by using esbuild to build api into a single out.js file. This includes all dependencies (therefore #myapp/server-lib.
The overall build process now looks like:
npx tsc --noEmit # checks types but does not output files
node build.js # uses esbuild to build the project
Where the build.js script is:
const nativeNodeModulesPlugin = {
name: 'native-node-modules',
setup(build) {
// If a ".node" file is imported within a module in the "file" namespace, resolve
// it to an absolute path and put it into the "node-file" virtual namespace.
build.onResolve({ filter: /\.node$/, namespace: 'file' }, args => ({
path: require.resolve(args.path, { paths: [args.resolveDir] }),
namespace: 'node-file',
}))
// Files in the "node-file" virtual namespace call "require()" on the
// path from esbuild of the ".node" file in the output directory.
build.onLoad({ filter: /.*/, namespace: 'node-file' }, args => ({
contents: `
import path from ${JSON.stringify(args.path)}
try { module.exports = require(path) }
catch {}
`,
}))
// If a ".node" file is imported within a module in the "node-file" namespace, put
// it in the "file" namespace where esbuild's default loading behavior will handle
// it. It is already an absolute path since we resolved it to one above.
build.onResolve({ filter: /\.node$/, namespace: 'node-file' }, args => ({
path: args.path,
namespace: 'file',
}))
// Tell esbuild's default loading behavior to use the "file" loader for
// these ".node" files.
let opts = build.initialOptions
opts.loader = opts.loader || {}
opts.loader['.node'] = 'file'
},
}
require("esbuild").build({
entryPoints: ["./src/server.ts"], // the entrypoint of the server
platform: "node",
target: "node16.0",
outfile: "./build/out.js", // the single file it will bundle everything into
bundle: true,
loader: {".ts": "ts"},
plugins: [nativeNodeModulesPlugin], // addresses native node modules (like fs)
})
.then((res) => console.log(`⚡ Bundled!`))
.catch(() => process.exit(1));
On my server, the start script in package.json is just node out.js and there are no dependencies or devDependencies since all are bundled into out.js.
I've separated out my 3rd party libraries from my app code and grouped them all together into a vendor.js file for requirejs to pull in. In my build.js file, I'm using the modules syntax to optimize my main application, excluding the vendor scripts, and to optimize the vendor.js file. The only issue I'm having is when my compiled main module requests vendor, it's getting the baseUrl from the config file and so doesn't load the optimized vendor.js file. My build.js file looks like this:
({
baseUrl: "js",
dir: "build",
mainConfigFile: "js/main.js",
removeCombined: true,
findNestedDependencies: true,
skipDirOptimize: true,
inlineText: true,
useStrict: true,
wrap: true,
keepBuildDir: false,
optimize: "uglify2",
modules: [
{
name: "vendor"
},
{
name: "main",
exclude: ["vendor"]
}
]
})
And my main.js file looks like this:
requirejs.config({
baseUrl: "js",
paths: {
jquery: 'vendor/jquery/jquery-2.1.3.min',
bootstrap: 'vendor/bootstrap/bootstrap.min',
handlebars: 'vendor/handlebars/handlebars-v2.0.0',
backbone: 'vendor/backbone/backbone-min',
underscore: 'vendor/lodash/lodash.underscore',
marionette: 'vendor/marionette/backbone.marionette.min',
models: 'common/models',
collections: 'common/collections'
}
});
define(['module', 'vendor'], function(module) {
var configPath = "config/config." + module.config().env;
require([configPath, 'app', 'jquery'], function(config, Application, $) {
$(function() {
// Kick off the app
Application.start(config);
});
});
});
All development is done in the js folder, and my build.js file is outside that folder. The optimized files end up in build, a sibling to js, but when I include my main file like this:
<script data-main="build/main" src="js/vendor/require/require.max.js"></script>
It ends up loading js/vendor.js for that define() call. What am I missing here? How can I tell the optimized main file to load build/vendor.js instead, yet allow the unoptimized version to still load js/vendor.js?
Ok, I figured this out. It was simple, really, just a case of too much configuration. When you load your script using data-main, the baseUrl is set relative to that file. So, if I specified js/main, the baseUrl would be js. But, since I explicitly specified baseUrl in the config block of main.js, that gets overridden, both in development and production. By removing baseUrl: "js" from main.js, everything works as expected. The development build loads everything relative to js and the production build loads everything (vendor.js) relative to build when I change data-main to build/main. Hope this helps somebody else someday.
requirejs.config({
paths: {
jquery: 'vendor/jquery/jquery-2.1.3.min',
...
}
});
// 'vendor' is loaded relative to whatever directory main.js is in
define(['module', 'vendor'], function(module) {
...
});
Hierarchy:
App
.tmp // temp files
app // source files
dist // dist files
So if I put the devcode:build before requirejs:
Files from "app/scripts" are processed and saved into ".tmp/scripts"
Requirejs will be pointed to load the ".tmp/scripts"
Then fails because bower_components are not found at "bower_components"; Of course, because bower_components are located in "app/bower_components"
If I inverse the order:
Requirejs removes the comments and devcode doesn't work
I will remove require.js optimizer and then my build is not ok. Should I pass another uglify over it.
Any better solution? (don't make the pc to copy bower_components all over again, or I might change the position up to the root?)
Thanks
Well I don't need the devcode grunt task becasue requirejs already comes with uglify2 which allows you to have the dist.options.uglify2.compress.global_defs
If you put DEBUG = false then this code will be removed in production mode.
dist: {
options: {
baseUrl : '<%= yeoman.app %>/scripts/',
name : 'main',
mainConfigFile : '<%= yeoman.app %>/scripts/main.js',
out : '.tmp/concat/scripts/main.js',
optimize : 'uglify2',
uglify2: {
//Example of a specialized config. If you are fine
//with the default options, no need to specify
//any of these properties.
output: {
beautify: false
},
compress: {
global_defs: {
DEBUG: false
}
},
warnings : true,
mangle : true
},
}
},
In my project I use yeoman (1.0.6). In a fresh webapp copy installed requirejs-text plugin to include template.html.
main.js
require.config({
paths: {
jquery: '../bower_components/jquery/jquery',
text: '../bower_components/requirejs-text/text'
}
});
require(['jquery', 'text!../templates.html'], function ($, templates) {
....
After building and optimizing a whole project, I expect to have generated templates.js file instead of templates.html ( added
"optimizeAllPluginResources: true" as described here )
Gruntfile.js ( won't paste all code, just optimization settings )
....
requirejs: {
dist: {
options: {
baseUrl: '<%= yeoman.app %>/scripts',
optimize: 'none',
optimizeAllPluginResources: true,
preserveLicenseComments: false,
useStrict: true,
wrap: true
}
}
},
....
After grunt 'build' task is completed I see that template.html content is in main.js and there is no generated templates.js file
After adding (also have to set in copy task to copy requirejs-text plugin form app to dir folder ):
stubModules: ['text'],
exclude: ['text!../templates.html'],
files are excluded as expected, but there is still no templates.js file. ( get an error as expected: XMLHttpRequest cannot load file:///...../dist/templates.html. Cross origin requests are only supported for HTTP. it works fine with local HTTP )
My question is: What settings am I missing to generate templates.js file with a requirejs optimizer?
p.s. googled, spent all day, tried more than wrote here.
Thank You in Advance
i want to exclude .coffee file in the app directory i am building with r.js.
So this statement in build.js(build.js is in the app dir) produces ERROR :
({
baseUrl: ".",
name: "main",
out: "../build/result.js",
stubModules: ['cs', 'text'],
exclude: ['coffee-script', 'myfile'], # ERROR, no such file myfile.js in app dir..
optimize: "none",
paths: {...}
})
you may get what you want by using fileExclusionRegExp: /\.coffee$/