I have a bundle building script using Rollup but inside my code I have a simple JSON "require" import that "rollup" is not resolving for some reason, and there are no errors, anyone is aware of the reason and how to resolve it (it leaves that code alone and just retains the original code without converting it)?
const translation = require('../translation.json');
My rollup config script "rollup.config.js" looks like this:
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import replace from '#rollup/plugin-replace';
import json from '#rollup/plugin-json';
export default {
input: 'src/javascript/script.js',
output: {
file: 'dist/script.js',
format: 'iife'
},
plugins: [
resolve(),
commonjs(),
replace({
'process.env.LANG': JSON.stringify(process.env.LANG)
}),
json()
]
};
My original intent was to resolve the require import with a dynamic variable that allows me to construct the file path in the following way:
const translation = require(`../translation_${process.env.LANG}.json`);
But I cannot even get the the simple require to resolve using Rollup
PS: I can get this to work with webpack but it creates so much code junk that it makes majority of the basic bundled code hard to read or in some cases unreadable, so trying to stay away from it
Related
Is there any way of building my svelte or react application in a way, that the three.js module (which I usually import using npm) will be declared as a script tag which will call the module from a CDN? I would like to keep the advantages of a framework but also be able to reduce my final bundle size, since most of my bundle contains three code.
Thank you for your wisdom
There are two ways to go about your goal of reducing bundle size:
Importing from a CDN (your suggestion)
Code-splitting
Importing from a CDN
To keep semantics of ESModules, you may simply replace your current three.js imports with a URL from an npm CDN, like unpkg:
Pros
Cons
No extra configuration needed
Slower to load, as browser needs to spin up new connections to access third-party CDN
Asynchronously
<script>
// App.svelte
import('https://unpkg.com/three#0.133.1/build/three.min.js').then(({ default: THREE }) => {
// your code here
});
</script>
Synchronously
Note: Importing like this blocks the rest of your script from loading while three.js is downloading, which defeats the purpose of the whole shebang. It's just here for completeness
<script>
// App.svelte
import { default as THREE } from 'https://unpkg.com/three#0.133.1/build/three.min.js';
// your code here
</script>
Code-splitting
This method takes advantage of the fact that you're already using a bundler (probably rollup, vite, or webpack). This answer will focus on rollup as it's the default used in svelte's examples.
Pros
Cons
Faster to load, as browser can use existing connections to access first-party resources
More complicated to get set up
Asynchronously
In your rollup.config.js file, ensure output.format is set to 'esm' & output.dir is set instead of output.file
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/index.js',
output: {
sourcemap: !production,
format: 'esm',
name: 'app',
dir: 'public',
},
plugins: {
// your plugins
svelte({
compilerOptions: {
dev: !production,
},
}),
postcss({
extract: 'bundle.css',
}),
resolve({
browser: true,
dedupe: ['svelte'],
}),
commonjs(),
}
}
<script>
// App.svelte
import('three').then(({ default: THREE }) => {
// your code here
});
</script>
Note: There is no synchronous way due to how code-splitting is evaluated at compile time. Plus it doesn't make much sense to do it like that anyways.
Yes, you can do the following:
In your "index.html" file, you can import the js file from a CDN as follow:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
Then, in the file where you want to use it, which could be a React component for instance, you can do the following:
const THREE = window.THREE;
Which would replace your import statement, which would have been import * as THREE from "three"; or import THREE from "three";
I have a config.ts and I do
module.exports = { something: 123 }
when I import like import { something } from './config.ts' I got error of config.ts' is not a module, what's the issue? my typescript is configured rightly and it's working in other places.
If you're using import { something } from './config.ts', you're using JavaScript modules, but your code in config.ts is using CommonJS modules. Some bundlers and such may let you mix them, but it's best not to.
To make config.ts a JavaScript module compatible with that import declaration (which expects a named export called something), change it to:
export const something = 123;
Or, of course, to use config.ts via CommonJS, your code using it would be:
const { something } = require("./config.ts");
...but given the error you're getting, I think your project is set up to use JavaScript modules (import/export), which here in almost 2021 is probably best (now that we have dynamic import to handle the cases where static modules don't quite do the job).
I'm trying to import a static .json file in the <script> section of a .Vue file like so import Test from '#assets/test.json'
Based on what I've read about webpack, this should work out of the box. I've also tried explicitly defining both the .json extension in webpack resolve and the json loader under loaders.
However, for me it fails with the error:
ERROR Failed to compile with 1 errors 9:14:24 AM
This dependency was not found:
* #assets/test.json in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Settings.vue?vue&type=script&lang=js&
To install it, you can run: npm install --save #assets/test.json
That seems to be strange as it's not a dependency I'd be able to install?
My test.json file just contains {}, but I've also tried it with the real data file and the same outcome.
Thanks in advance for any help.
Edit: Thanks to #tao for helping me figure it out on chat. The error was indeed trivial: import Test from '#assets/test.json' should have been import Test from '#/assets/test.json'
For typescript support you have to add
"resolveJsonModule": true,
to compilerOptions in tsconfig.json.
And add
declare module '*.json' {
const value: unknown;
export default value;
}
to your shims-vue.d.ts
In Javascript, you don't need to do anything special. This should work:
test.json:
{
"foo": "bar"
}
Your *.vue SFC file:
<script>
import * as json from './test.json';
export default {
data: () => ({
json
}),
mounted() {
console.log(this.json)
}
}
</script>
I currently have a large private NPM library which is being consumed by several other teams' apps across the business. At the moment the library is being published as one large single file (like the main lodash file) but this is causing application bundle size to be bloated as some of the applications don't need a large chunk of what is in the library.
So at the moment the apps are importing something like this
import { SomeReactComponent, someHelperFunction } from 'my-private-library';
What I want to achieve is the library published with individual modules similar to how Lodash, so the above would become:
import SomeReactComponent from 'my-private-library/lib/SomeReactComponent';
import someHelperFunction from 'my-private-library/lib/someHelperFunction';
I can get Webpack to output output the library in this format using multiple entry points, but what I can't get to work is getting Webpack to split out shared dependencies of each of those modules. So say the files look something like this:
src/SomeReactComponent.jsx
import React from 'react'
import SOME_CONST_STRING from '../constants';
const SomeReactComponent = () => {
return (
<div>You are using {SOME_CONST_STRING}</div>
);
}
export default SomeReactComponent;
src/someHelperFunction
import SOME_CONST_STRING from '../constants';
export default function someHelperFunction() {
return `This is just an example of ${SOME_CONST_STRING}`;
}
My Webpack is outputting the individual files, but it's not splitting out common code in a way that an app can consume the library. So notice above the SOME_CONST_STRING which is imported in each of the modules, Webpack is putting this code in both of the exported files.
My Webpack config looks a bit like this (removed other setting for brevity)
module.exports = {
entry: {
SomeReactComponent: './src/SomeReactComponent',
someHelperFunction: './src/someHelperFunction',
},
output: {
path: './lib',
library: 'MyPrivateLibrary'
libraryTarget: 'umd',
filename: '[name].js'
}
// removed other setting for brevity
}
I have tried using the splitChunks optimization setting like this
module.exports = {
entry: {
SomeReactComponent: './src/SomeReactComponent',
someHelperFunction: './src/someHelperFunction',
},
output: {
path: './lib',
library: 'MyPrivateLibrary'
libraryTarget: 'umd',
filename: '[name].js'
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
// removed other setting for brevity
}
which does chunk the code, but when I try to use the library in an app after doing this I get errors along the lines of (ERROR in TypeError: __webpack_require__(...) is not a function).
My question is can anyone see what I'm doing wrong? Is what I'm trying to achieve even possible with Webpack? Are there any example out there (as I can't find any) on how to do this?
Apologies for the example code, as my library is private I'm not able to use real-code examples.
Did you get success to achieve what you were trying to achieve in above scenario. I am working on the same use case and was facing similiar issue. After diagnosing it i found it that when we define library then in parsed module webpack add this in this object as window.myWebpackJsonpMyPrivateLibrary in minified main chunk which is undefined. if you remove the library and libraryTarget from webpack then you will not face this issue.
In my case i faced another issue that required chunk(s) are not being loaded when this is used as install dependency in another project.
I have this situation where I am trying to import an existing library, which I'll call troublesome (using Webpack/Babel FWIW) and it has a global reference to jQuery in it which i am trying to resolve using module syntax.
I have successfully imported jquery into the 'local' scope of a module, via:
import jQuery from 'jquery'
so I tried:
import jQuery from 'jquery'
import 'troublesome'
but perhaps not surprisingly, I get something like jQuery is not a function kicked back from troublesome.js
I have tried this as well:
System.import('jquery')
.then(jQuery => {
window.jQuery = jQuery
})
import 'troublesome'
but, it turns out that System.import is part of the, so-called, 'module-loader' spec, which was pulled from the es6/2015 spec, so it isn't provided by Babel. There is a poly-fill, but Webpack wouldn't be able to manage dynamic imports accomplished via calls to System.import anyway.
but... if I call out the script files in index.html like so:
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/troublesome/troublesome.js"></script>
<script src="the-rest-of-my-js.js"></script>
the reference to jQuery is resolved in troublesome.js and things are good,
but I would prefer to avoid the script tag route as webpack doesn't manage those.
Can anyone recommend a decent strategy for dealing with scenarios like this?
update
with some guidance from #TN1ck, I was eventually able to identify one Webpack-centric solution, using the imports-loader
The configuration for this solution looks something like this:
//...
module: {
loaders: [
//...
{
test: require.resolve('troublesome'),
loader: "imports?jQuery=jquery,$=jquery"
}
]
}
Shimming modules is the way to go: http://webpack.github.io/docs/shimming-modules.html
I quote from the page:
plugin ProvidePlugin
This plugin makes a module available as variable in every module. The module is required only if you use the variable.
Example: Make $ and jQuery available in every module without writing require("jquery").
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
To use this with your webpack-config just add this object to an array called plugins in the config:
// the plugins we want to use
var plugins = [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
];
// this is your webpack-config
module.exports = {
entry: ...,
output: ...,
module: ...,
plugins: plugins
}
For es6/2015 I done the following.
import {jsdom} from 'jsdom';
import jQuery from 'jquery';
var window = jsdom(undefined, {}).defaultView;
var $ = jQuery(window);
//window.jQuery = $; //probably not needed but it's there if something wrong
//window.$ = $;//probably not needed but it's there if something wrong
Then you can use it as normal
var text = $('<div />').text('hello world!!!').text();
console.log(text);
Hope this helps.
Importing jQuery into your module does not make it available for 'troublesome'. Instead, you could create a thin wrapper module for 'troublesome' that provides jQuery and any other required "globals".
troublesome-module.js:
// Bring jQuery into scope for troublesome.
import jQuery from 'jquery';
// Import any other 'troublesome'-assumed globals.
// Paste or have build tool interpolate existing troublesome.js code here.
Then in your code you should be able to
import 'troublesome-module';
I've had a similar issue using jspm and dygraphs. The way i solved it was to use dynamic loading like you attempted using System.import but the important part was to chain-load each consecutive "part" using System.import again inside the promise onfulfillment handler (then) after setting the global namespace variable. In my scenario I actually had to have several import steps separated between then handlers.
The reason it didn't work with jspm, and probably why it didn't work for you as well is that the import ... from ... syntax is evaluated before any code, and definitely before System.import which of async.
In your case it could be as simple as:
import jQuery from 'jquery';
window.jQuery = jQuery;
System.import('troublesome').then(troublesome => {
// Do something with it...
});
Also note that the System module loader recommendation has been left out of the final ES6 specification, and a new loader spec is being drafted.
run npm install import-loader.
replace import 'troublesome' with import 'imports?jQuery=jquery,$=jquery!troublesome.
In my opinion, this is the simplest solution to your question. It is similar to the answer you wrote in your question #TN1ck, but without altering your webpack config. For more reading, see: https://github.com/webpack/imports-loader
Shimming is fine and there are various ways of resolving this, but as per my answer here, the simplest is actually just to revert to using require for the loading of the library that requires the global dependency - then just make sure your window. assignment is before that require statement, and they are both after your other imports, and your ordering should remain as intended. The issue is caused by Babel hoisting imports such that they all get executed before any other code.