How to disable Tree shaking in rollupjs - javascript

I am trying to bundle together several javascript files using RollUp.js but when I do, the classes that aren't used get removed. This process is called tree shaking and I want to disable it.
I have found this but it doesn't seem to have any effect.
// rollup.config.js
let configuration = {
output: {
format: 'es',
},
name: 'namename',
input: './main.js',
treeshake: false, // <-- disabling tree shaking?
};
export default configuration;
I added treeshake: false to the configuration, but it doesn't seem to have any effect. Is this supposed to be placed somewhere else?
Here are the files I am trying to roll up.
// Base.js
export default class Base {
aMethod() {
return "Hello";
}
}
// main.js
import Base from './Base.js';
So with this set up, I call rollup --config and it produces something empty. So clearly, tree shaking is happening and it is removing the Base class even though I imported it.
So far the only workaround I've found is to actually create an instance of the class, which is undesirable.
// main.js
import Base from './Base.js';
export default function () {
{
new Base();
}
}
My goal is to use the bundled javascript file with JSContext. It will take in the javascript as a string and then from there, I'd invoke methods as needed.
// suppose rollup.js produces a file called "product.js"
let s = String(contentsOfFile: "path/to/product.js")
let context = JSContext()!
context.evaluateScript(s)
context.evaluateScript("var b = new Base()")
context.evaluateScript("b.aMethod()")
But because of the tree shaking the Base class never gets placed in product.js
Is there a way to disable tree shaking?
I've included a sample project for this.

Your entry file — main.js — needs to export any classes or other values that need to be accessible to the outside world:
// main.js
import Base from './Base.js';
import SubB from './SubB.js';
import SubA from './SubA.js';
export { Base, SubA, SubB };

Related

JS: How to share static class properties between imports

so I am running into this problem while I am converting some old js to new es6 imports (used in the browser).
The problem:
The static properties of a class are not shared between the various imports in different files.
(Even though they are bundled into one file eventually (using Vite)).
A simplified version of the code:
// scripts.ts (will be three-shaked, compiled and bundled using Vite)
// This file will be loaded in a simple index.html
import Base from "./Base";
import Form from "./Form";
window.Base = Base;
window.Form = Form;
document.addEventListener("DOMContentLoaded", () => {
Base.setRoot("myroot");
console.log(Base.root); // 'myroot'
Form.logRoot(); // 👈 'default', but expected 'myroot'
});
// base.ts
export default class Base {
static root: "default"
static setRoot(root) {
this.root = root;
}
}
// form.ts
import Base from "./Base"
export default class Form {
static logRoot() {
// attempt 1
console.log(Base.root); // doesn't work
// attempt 2
console.log(window.Base.root); // works, but undesirable: TS throws error that window.Base is undefined
}
}
The question:
So how can I share the static properties of this Base class between the various imports, without having to pull it from window.Base?
Perhaps a little background:
These classes used to be just two global var objects, that got bundled together, without using any imports or anything. Thus the state of the vars would be shared because they basically just lived on the window.

Importing / exporting Javascript Object Properties

I support a relatively complex legacy codebase, but am looking to modernise it a little by bringing in Webpack so that we'd have import & export capabilities in JS.
The problem I'm having is that we use a global object called App where we define and add different properties depending on the page. So for example we have the following file where we instantiate App (loaded on all pages):
app.js
const App = (() => {
const obj = {
Lib: {},
Util: {},
// etc
}
return obj;
})();
Then in another file we add to App.Lib just for the specific page that needs it:
lazyload.js
App.Lib.Lazyload = (() => {
// lazyload logic
})();
We simply concatenate the files during the bundling process, but obviously this is not ideal as none of the files have no knowledge of what goes on outside of it.
Exporting only seems to work for the top level object (where the object is defined), so anything I add to it elsewhere cannot be exported again. For example if I add export default App.Lib.Lazyload; at the end of lazyload.js and then try to import it elsewhere it will not import the Lazyload property.
Is there any way to get this to work without major refactor? If not, would you have any suggestions about the best way to handle it?
I don't think you can import Object.properties in JS. If you want to bundle specific packages (say Lazyload) for packages that need them, you might try:
//lazyload.js
export const LazyLoad = {
//lazyload logic
}
then somewhere else...
import {LazyLoad} from 'path/to/lazyload.js';
// assuming App has already been created/instantiated
App.Lib.Lazyload = LazyLoad;
Using Export Default...
//lazyload.js
const LazyLoad = {};
export default LazyLoad;
then...
import LazyLoad from 'path/to/lazyload.js';
App.Lib.LazyLoad = LazyLoad;
You can find help with Imports and Exports at MDN.

TypeScript classes and Webpack

Let say I have a typescript class with 10 methods and that the file export a new instance of the class as its default export. Then I have another file, like a React functional component, that import this class and call one method on the class.
How will this be optimized? Can Webpack/Babel extract the code for just the method used, or will it include the whole class and I will have a bunch of unused code?
Is it better to avoid classes and export each function instead?
My goal is to make the exported bundles smaller and also have Lighthouse complain less about unused JavaScript.
Most tree shaking tools (including Webpack) work by analysing the tree of ES6 imports and exports in order to tree shake unused exports.
Take the following example:
export class {
myfunc1() { /* do stuff */ }
myfunc2() { /* do stuff */ }
}
When tree shaking with Webpack, if myFunc2 is used somewhere, myFunc1 cannot be tree shaken even if it is not used.
But here, either function could be tree shaken if not used:
export myFunc1 = () => { /* Do stuff */}
export myFunc2 = () => { /* Do stuff */}
In this case it is better for tree shaking (with Webpack) to use functions grouped together in a file, rather than a class.

Scope import for an instance only

Good evening to everyone.
I'm not sure how can I explain my issue. I will show it to you by showing examples of the code and expected results. I could not use code from the real issue because the code is under license. I am very sorry for that and I will be glad of someone can help me solve my issue.
I'm using latest version of webpack, babel.
My application is spliced to three parts what are dynamically imported by each other. It is mean if I run split chunks plugin it will really create three clear files.
The parts are Core, Shared, Application. Where the Core only creating an instance of the application.
Result of the parts is bundled to single file. So it is linked by one html's script tag.
Project structure is:
src/app // For Application
src/core // For Core
src/shared // For Shared
In webpack configuration I am resolving alias for import ˙Editor$˙.
I renamed naming of variables because they are including project name.
resolve: {
alias: {
"Editor$": path.resolve('./src/app/statics/Editor.js'),
}
},
The content of Core file is
function createInstance(name, id) {
import("app").then(App => {
App(name, id)
});
}
The little bit of Application file is
imports...
import Framework from "./framework"
function createApp(name, id) {
new Framework({name, id}).$mount(...)
}
export default createApp
In the Application classes (what are instantiated inside Framework)
Is this import
import Editor from "Editor"
The Editor class is a singleton. But only for created instance.
class Editor {
static instance;
id = null;
constructor(){
if(this.constructor.instance){
return this.constructor.instance
}
this.constructor.instance = this
}
static get Instance() {
return this.instance || (this.instance = new this())
}
static get Id {
return this.Instance.id;
}
}
export default Editor
The issue is webpack dependency resolving. Because webpack puts and unify all imports to the top of the file.
So the imports are evaluated once through the life-cycle of the program.
But I need to tell webpack something like: There is an instance creation. Declare the new Editor singleton for this scope. Don not use the already cached one.
My another idea how to fix this is to set context for the instance. And in the Editor singleton create something like new Map<Context, Editor> if you get what I mean. But I did not find a way how to set a context for an instance or scope the import only for it.
I will appreciate any help. I am googling two days and still no have idea how to do it without rewriting all the imports.
Sorry for bugs in my English. I am not native speaker and my brain is not for languages.
Thanks everyone who take look into my issue.
How about recreating the Editor:
// Editor.js
class Editor {
// ...
}
let instance;
export function scope(cb) {
instance = new Editor();
cb();
instance = null;
}
export default function createEditor() {
if(!instance) throw Error("Editor created out of scope!");
return instance;
}
That way you can easily set up different scopes:
// index.js
import {scope} from "./editor";
scope(() => {
require("A");
require("B");
});
scope(() => {
require("C");
});
// A
import Editor from "./editor";
(new Editor()).sth = 1;
// B
import Editor from "./editor";
console.log((new Editor()).sth); // 1
// C
import Editor from "./editor";
console.log((new Editor()).sth); // undefined
// note that this will fail:
setTimeout(() => {
new Editor(); // error: Editor created out of scope
}, 0);
That also works for nested requires and imports as long as they are not dynamic.

Importing external dependencies in Typescript

I am a newbie in typescript/node. I have a typescript file "order.ts" from which I would like to import an external config dependency from "config.ts"
My config file code is as below
let config = {
mongoAddress: "mongodb://localhost:27017/dts",
dataDirectory: "../../data/"
};
module.exports = config;
I am importing the config file in the order file as below
import { config } from "../../config";
However I am getting the TS compiler throwing error "... file not a module". Any clues how I should I be importing my external dependency in typescript
The main part here is you want to export your object instance. You're on the right track with that, but there may be an easier way for you.
In this case something like wrapping it in a class and exporting that:
export class Config {
mongoAddress = 'mongodb://localhost:27017/dts';
dataDirectory = '../../data/';
}
Notice the export before class. The same can be applied to interfaces, enums etc. By exporting it this way you can then import it and initialise it:
import { Config } from '../config';
var c = new Config();
console.log(c.mongoAddress);
This will not make it a variable, like in your original example, but you'll simply wrap it in a class. This is also why you have to initialise it first using new Config().
Now, I'm assuming you want these properties simply to be accessed globally. And perhaps even static/readonly, so you don't have to initialise the class each time. Making use of the static typing of TypeScript, the sample would in this case be better refactored to something like this:
export class Config {
public static readonly mongoAddress: string = 'mongodb://localhost:27017/dts';
public static readonly dataDirectory: string = '../../data/';
}
With this, calling it is even less obtrusive - and very type safe:
console.log(Config.mongoAddress);
console.log(Config.dataDirectory);
Now exporting this way is just one of the options. It actually depends entirely on the library structure you're using throughout your application (or from third partie libraries, for that matter). It's a bit of dry reading, but I recommend you have a look at the different structures to get acquainted with terms like UMD and modules and how they relate to an import.
Hope this helps!
There are 2 ways you can do import and export.
1) default export
// config.ts
export const config = {
mongoAddress: "mongodb://localhost:27017/dts",
dataDirectory: "../../data/"
};
export default config;
// your other file
import configs from './config';
Note: Here you can give any name for the imported module;
2) normal export with exact declaration name while importing.
// config.ts
export const config = {
mongoAddress: "mongodb://localhost:27017/dts",
dataDirectory: "../../data/"
};
// your other file
import { config } from './config';
Note: Here you have to give the exact name of the module that you exported.
Best practices to follow while exporting configs.
create a static class with static variables in the code. Which likely means that these configs are fixed stuffs.
module.exports is the node syntax for exporting modules. Typescript has a keyword names export so you can just use this:
export const config = {
mongoAddress: "mongodb://localhost:27017/dts",
dataDirectory: "../../data/"
};

Categories

Resources