Confused between modules and bundles property in RequireJS / r.js build files - requirejs

I am very confused at to what the difference is between the "bundles" property and the "modules" property in a r.js build file that might look something like:
({
"allowSourceOverwrites": false,
"preserveLicenseComments": false,
"findNestedDependencies": false,
"optimizeAllPluginResources": true,
"dir":"../public/bundles",
"baseUrl": "../public/static",
"optimize":"none",
"mainConfigFile": "../public/static/app/js/main.js",
"normalizeDirDefines": "all",
"paths" :{
requireLib : 'vendor/require',
jqueryUI: "empty:",
jqueryUICSS: "empty:"
},
"modules": [
{
name: "shared",
include: [
'jquery',
'async',
'backbone'
],
exclude:[]
}
],
"bundles": {
'shared': [],
'secondary': []
},
"stubModules":['text']
})
I am having a lot of trouble finding good information on how to create multiple bundles for a RequireJS project. When I just use the modules property and eliminate the bundles property, r.js goes looking for a file called shared.js - and I am not sure why.

r.js creates bundles, but the bundles option is not an option that r.js recognizes. If you look in the file that lists all r.js options, you won't find bundles there. If you remove it from your configuration, you'll get the same behavior.
When I just use the modules property and eliminate the bundles property, r.js goes looking for a file called shared.js - and I am not sure why.
r.js looks for shared.js because your modules specifies a module named "shared". r.js will do this unless you tell it that you want to create this module from scratch, and you do this by adding the create: true option to this module's build configuration:
"modules": [
{
name: "shared",
create: true, // <<<--- add this!
include: [
'jquery',
'async',
'backbone'
],
exclude:[]
}
],

Related

Bundle NPM Package so it has different import paths with Vite and Typescript

How can I bundle my NPM package in a way that I can have different import paths for different parts of the package? I have found webpack approaches, but I am using Vite and TS.
My package looks like this:
- src
- atoms
- molecules
- organism
- index.ts (currently simply imports and exports everything)
Now I can use this currently like this
import { Button } from '#mypackage/library'
How can I do it, so I get this outcome:
import { Button } from '#mypackage/library/atom'
Here is the relevant part of my package.json
{
"entry": "src/index.ts",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"src"
],
"exports": {
".": {
"import": "./dist/index.es.js",
"require": "./dist/index.cjs.js",
"types": "./dist/index.d.ts"
},
"./package.json": "./package.json",
"./atoms": "./src/atoms/index.ts",
"./molecules": "./src/molecules/index.ts",
"./organisms": "./src/organisms/index.ts",
"./theme": "./src/theme/index.ts"
},
}
Here is my vite.config.ts
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
formats: ['es', 'cjs'],
name: '#workdigtital/component-library-react',
fileName: (format) => `index.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
},
exports: 'named'
}
}
},
plugins: [react(), dts({ insertTypesEntry: true })],
resolve: {
alias: {
'#': path.resolve(__dirname, './src')
}
}
});
If I currently try an import like this, inside another project (Laravel+React), in which installed the library.
import { ThemeProvider } from '#workdigital/component-library-react/theme';
I get the following run time error (But no Typescript errors, even IntelliSense is working):
Failed to load url /resources/js/theme/ThemeProvider (resolved id: /resources/js/theme/ThemeProvider). Does the file exist?
My resulting Dist folder looks like this:
You can't have TypeScript exports, this simply won't work. An npm package should have only JS exports.
If you want to be able to selectively import different parts of your package, you must transpile them to different files.
rollup can do it, but it is lots of work, as you will have to set up a separate target for each exported file. Normally you use rollup to create a single bundle, this what this tool is made for.
tsc with a tsconfig.json will be a much better choice in your case. It does this by default, you only need to specify the output directory and it will produce a separate file for each source.
There is an excellent guide on the TypeScript site about packaging TypeScript libraries, you should probably start there.

Jest Is Not Transpiling ES6 Modules In Test (SyntaxError: Unexpected token export)

I'm at a boiling point with getting Jest to understand ES6 modules import/export syntax and it is hindering my project development progress. My project structure is the following:
root module
org-domain (ES6)
org-services (ES6)
react-ui-module (React via CRA2)
org-services has a local path dependency on org-domain in its package json:
// in org-services package.json
"dependencies": {
"#org/domain": "file:../org-domain",
},
My .babelrc in org-services is the following:
{
"env": {
"test": {
"presets": ["#babel/preset-flow", "#babel/preset-env"]
"plugins": ["#babel/plugin-transform-modules-commonjs"]
}
},
"presets": [
"#babel/preset-flow",
["#babel/preset-env", {
"targets": {
"esmodules": true
}
}]
],
"plugins": [
["module-resolver", {
"root": ["./node_modules/#org/domain"],
"alias": {
"#org/constants": "./node_modules/#org/domain/src/constants",
"#org/contracts": "./node_modules/#org/domain/src/request-contracts"
}
}]
]
}
I do not know if the problem is due to how I am including my dependencies so I'm going to add the finer details of anything related to my import/export of these modules for the sake of clarity.
In the implementation files of org-services I am importing org-domain using npm scoped syntax like so: import ... from '#org/domain
Here are some observations I have:
In local development, when I try to reference click #org/domain, instead of being directed to org-services/node_modules/#org/domain I get redirected to the actual relative directory location which is root/org-services. I believe Jest ignores node_modules (correct me if I am wrong) but in my jest.config.js for org-services, I have:
collectCoverage: true,
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: [
'/node_modules/'
],
moduleDirectories: [
'src',
'node_modules'
],
moduleFileExtensions: [
'js'
],
transform: {
'^.+\\.js$': 'babel-jest'
},
transformIgnorePatterns: [
'node_modules/(?!#org/domain)$'
]
To my understanding, everything should just work right now with all the configuration I have with respect to setting the plugin #babel/plugin-transform-modules-commonjs in test (within .babelrc) and including the '^.+\\.js$': 'babel-jest' instruction under the transform key in jest.config.js located under org-services -- but it does not.
I have tried every single thing I could find online with respect to this issue with no success. I have not gotten anywhere since and my patience is lost with this testing framework and the lack of support for ES6. It should not be this hard, so clearly I am doing something wrong here. Please advise.
Update 1
Found another SO post that is a mirror of this situation I am in.
Jest fails to transpile import from npm linked module
Unfortunately, the provided solution does not work for my case.
After upgrading from #babel/xyz: 7.0.0 to 7.1.2 I started getting an error regarding "import"
Jest encountered an unexpected token
<snip>
Details:
C:\....\SstcStrategy.test.js:2
import sequelizeFixtures from 'sequelize-fixtures';
^^^^^^
SyntaxError: Unexpected token import
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
To fix this I had to add #babel/plugin-transform-modules-commonjs as you mention in your question.
My babel.config.js now looks like this:
module.exports = {
presets: [
[
'#babel/preset-env',
{
targets: {
node: '8.10',
},
debug: false,
},
],
],
ignore: ['node_modules'],
plugins: [
'#babel/plugin-transform-runtime',
'#babel/plugin-transform-modules-commonjs',
// Stage 2
['#babel/plugin-proposal-decorators', { legacy: true }],
'#babel/plugin-proposal-function-sent',
'#babel/plugin-proposal-export-namespace-from',
'#babel/plugin-proposal-numeric-separator',
'#babel/plugin-proposal-throw-expressions',
// Stage 3
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-syntax-import-meta',
['#babel/plugin-proposal-class-properties', { loose: false }],
'#babel/plugin-proposal-json-strings',
],
};
Also BTW you don't need to define babel-jest transform in jest.config.js as this is the default setting.
Hope this helps

Webpack with a client/server node setup?

I'm trying to set up a webpack-based flow for an Angular2 app with a node backend server. After many hours banging my head against it, I've managed to get the client to build happily, but I can not figure out how to now integrate my server build. My server uses generators, so must target ES6, and it needs to point to a different typings file (main.d.ts instead of browser.d.ts)..
My source tree looks like;
/
-- client/
-- -- <all my angular2 bits> (*.ts)
-- server/
-- -- <all my node/express bits> (*.ts)
-- webpack.config.js
-- typings/
-- -- browser.d.ts
-- -- main.d.ts
I thought perhaps just a tsconfig.json in the client and server folders would work, but no luck there. I also can't find a way to get html-webpack-plugin to ignore my server bundle and not inject it into index.html. My current tsconfig and webpack are below, but has anyone succeeded in getting webpack to bundle a setup like this? Any pointers would be much appreciated.
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"declaration": false,
"removeComments": true,
"noEmitHelpers": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
"files": [
"typings/browser.d.ts",
"client/app.ts",
"client/bootstrap.ts",
"client/polyfills.ts"
]
}
and my webpack.config.js;
var Webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var Path = require('path');
module.exports = {
entry: {
'polyfills': Path.join(__dirname, 'client', 'polyfills.ts'),
'client': Path.join(__dirname, 'client', 'bootstrap.ts')
},
output: {
path: Path.join(__dirname, 'dist'),
filename: '[name].bundle.js'
},
resolve: {
extensions: ['', '.js', '.json', '.ts']
},
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader',
query: {
ignoreDiagnostics: [
2403, // 2403 -> Subsequent variable declarations
2300, // 2300 -> Duplicate identifier
2374, // 2374 -> Duplicate number index signature
2375, // 2375 -> Duplicate string index signature
]
}
},
{ test: /\.json$/, loader: 'raw' },
{ test: /\.html$/, loader: 'raw' },
{ test: /\.css$/, loader: 'raw!postcss' },
{ test: /\.less$/, loSWE: 'raw!postcss!less' }
]
},
plugins: [
new HtmlWebpackPlugin({ template: 'client/index.html', filename: 'index.html' }),
new Webpack.optimize.CommonsChunkPlugin('common', 'common.bundle.js')
]
};
Personally, I tend to write my server side code in plain JS (with most of ES2015 available now in Node) and my Angular 2 app in Typescript, so this issue doesn't come up. However, you can get this to work with Webpack.
First, you should have two separate Webpack configs: one for your client-side code and one for the server side. It might be possible to do it with one config, but even if it were, it would likely be more trouble than it's worth. Make sure to set target: 'node' in your server-side config (target: 'web' is set automatically for the client side). And make sure you set an entry point for your server-side files (I don't see one above, but you will ultimately have this in a separate config anyway).
Second, you need to have multiple tsconfig files. By default, ts-loader will look for tsconfig.json in your root directory. However, you can tell specify another file by setting configFileName: 'path/to/tsconfig' in the options object or query string/object.
This may lead to another problem however. Your IDE will also look for your tsconfig.json file in your root directory. If you have two separate files, you will need some way to tell your IDE which one to use for any given file. The solution to this will depend on your IDE. Personally, I use Atom with atom-typescript, which is fantastic, but it looks like the multiple tsconfig files thing is still being worked on. Thankfully I have never had to worry about this problem.
As for the html-webpack-plugin issue, you won't have to worry about it since you won't include the plugin in your server-side config. However, just for reference, you can pass excludeChunks: ['someChunkName'] to omit certain chunks from being included in the script tags.

Should requirejs be included in the main built file when using the requirejs bundles option

I'm running into an issue when using the require bundles option. If the main built file has requirejs inside of it everything works fine until I try to load a file from a different bundle. The bundled file is retrieved but then throws an "define is undefined" error. The only way I have been able to get the bundle to load is to make sure requirejs is not in the main-built file or the pm.js and then to load requirejs with a script tag and use the data-main attribute, but this doesn't seem right.
So something like this initially works when requirejs is included in main-built.js (site loads fine), but I get the "define is undefined" error when pm.js bundle loads
<script type="text/javascript" src="~/dist/main-built.js"></script>
requirejs.config({
bundles: {
'pm': ['pm/dashboard', 'text!pm/dashboard.html']
}
});
This is how I ended up getting it to work, but doesn't seem right.
<script type="text/javascript" src="~/scripts/require.js" data-main="dist/main-debug")"></script>
This durandal task creates the main-built file
durandal: {
main: {
src: ["app/**/*.*", "scripts/durandal/**/*.*", "!app/mockup/**/*.*", "!app/performancemanagement/**/*.*"],
options: {
//name: "scripts/require",
name: "",
baseUrl: requireConfig.baseUrl,
paths: mixIn({}, requireConfig.paths, { "require": "scripts/require.js" }),
exclude: ["jquery", "knockout", "toastr", "moment", "underscore", "amplify"],
optimize: "none",
out: "dist/main-debug.js"
}
},
},
This task builds the pm.js bundle
requirejs: {
compile: {
options: {
include: generateFileList("app/pm", "**/*.*", false, false),
//exclude: ["jquery", "knockout", "toastr", "moment", "underscore", "amplify", "preferenceconstants", "constants", "config", "utility/koutilities", "scripts/logger", "base/viewmodel"]
// .concat(generateFileList("scripts/durandal", "**/*.js", false))
// .concat(generateFileList("app/dataservice", "**/*.js", false))
// .concat(generateFileList("app/model", "**/*.js", false))
// .concat(generateFileList("app/reports", "**/*.js", false)),
baseUrl: "app/",
name: "",
paths: mixIn({}, requireConfig.paths, { "almond": "scripts/almond-custom.js" }),
optimize: 'none',
inlineText: true,
pragmas: {
build: true
},
stubModules: ['text'],
out: "dist/pm.js"
}
}
}
The pm.js bundle gets downloaded and executed when anything in main-built requires it, right now its being done by the router in Durandal, but I'm pretty sure Durandal has nothing to do with the issue.
This appears suspicious in your main file build:
paths: mixIn({}, requireConfig.paths, { "require": "scripts/require.js" }),
I'm not sure what the mixIn bit does as this is not stock RequireJS code, but you seem to want to include RequireJS in the build under the name require, which is definitely wrong. The documentation says:
If you want to include require.js with the main.js source, you can use this kind of command:
node ../../r.js -o baseUrl=. paths.requireLib=../../require name=main include=requireLib out=main-built.js
Since "require" is a reserved dependency name, you create a "requireLib" dependency and map it to the require.js file.

require cdn libraries in browserify without bundling it in the final js file

If I have a library that is being pulled down from cdn and wouldn't like it to be part of the final js file but be able to require it using browserify, how would I solve it?
Here is how I currently solve it using alias and a shim file.
browserify: {
options: {
debug: true,
transform: [ 'reactify' ],
alias: [
'client/shims/jquery.js:jquery'
]
},
app: {
src: 'client/app.js',
dest: 'public/app.js'
}
}
here is the shim file client/shims/jquery.js which I alias to jquery so I can use require('jquery') instead of the full path.
module.exports = $;
Is there a shortcut in grunt-browserify to support this scenario? I would like to know if it is possible to define it in Gruntfile.js without creating the shim file.
Adding external: [ 'jquery' ] seems to totally ignore it and doesn't work.
With browserify-shim you can add this in your package.json file:
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"jquery": "global:$"
}
Then jquery will be available in your modules via require('jquery')
If you load jQuery before the Browserify bundle on the page, $ will be available as a global.

Resources