I have an angular 2 app which every client can slightly modify before receiving the production version. Because the differences are not too big, all instances of app are build of the same source code. To achieve diffrent builds i use webpack with NormalModuleReplacementPlugin,
webpack.config
new webpack.NormalModuleReplacementPlugin(/(.*)ENV(.*)/, function(resource) {
var componentName = resource.request.slice(resource.request.lastIndexOf('/') + 1, resource.request.indexOf('ENV'));
if(componentName in buildConfig){
resource.request = resource.request.replace(/ENV/, buildConfig[componentName]);
}
else {
resource.request = resource.request.replace(/ENV/, '');
}
})
import
import { RegisterComponent } from '../register/registerENV.component';
this works but also causes compiler and ide errors, which are preety annoying. So does anyone know better way to conditionaly build app with posibility to swap components and also not load unnecessary code into the bundle?
English is not my mother tongue, please excuse any errors on my part.
Related
I am currently developing an electron app, using typescript.
As the renderer is sandboxed for security reasons, I need to pass a single .js file to the main Window.
However, due to the script being more than 2500 lines, it is currently split in multiple imported files, as would be a normal project.
How could I then merge the js files produced by typescript into a single complete file that could be passed to the electron Window ?
I tried Webpack, but I'm starting to think this is not the tool designed to do it.
what I am looking for:
entry.ts
import { Thing } from "./Thing"
const thing = new Thing();
thing.doSomething("hello world");
Thing.ts
export class Thing {
doSomething(text: string) {
// Stuff
}
}
that would be processed into a single file:
main.js
class Thing {
doSomething(text) {
// Stuff
}
}
const thing = new Thing();
thing.doSomething("hello world");
Thanks for the help !
I have a relatively simple requirement to start out with.
I want to be able to load a particular UI component, based on some dynamic context. I was thinking that dynamic imports could be used for this purpose. The dynamic imports should be loaded through the Web browser (no nodeJS).
Details about what is possible with dynamic imports are very sketchy to me - at best, probably also because I'm not an expert in the field of JavaScript/TypeScript (yet).
I'm using vanilla JS at the moment and vitejs as build tool.
Here is what I have so far.
This is my main.ts file:
const getPath = () => {
if (import.meta.env.MODE === 'development') {
return 'http://127.0.0.1:3000/one.js';
} else {
return 'http://127.0.0.1:5000/one.js';
}
};
import(getPath()).then((Module) => {
Module.default();
});
This already makes vitejs barf, complaining that it cannot analyze things - but I'm ignoring that for now. The one.js file looks like this:
const hello = () => {
console.log('Hello from one');
};
export default hello;
Both run dev as run serve work with this setup, as-in, the message is printed on the browser console.
My next thing I want to see working is how one.js would be able to itself import a module and work with that. I've tried this with moment like so:
import moment from "moment";
const hello = () => {
console.log('Hello from one ', moment().format());
};
export default hello;
This errors with:
Uncaught (in promise) TypeError: Error resolving module specifier “moment”. Relative module specifiers must start with “./”, “../” or “/”.
Now I don't know whether what I want to do, is not support or that I'm just not doing it the right way. Any pointers would be appreciated.
I have several menu types and want to configure the type of menu to be used in .env.local for example: VUE_APP_MENU_TYPE=2
In my javascript file I have the following:
let menu = false;
if (process.env.VUE_APP_MENU_TYPE === "2") {
menu = require("./type2/Type2.vue");
}
if (menu === false) {//default menu if env is missing
menu = require("./default/Default.vue");
}
export default menu;
This will result in an error Failed to mount component: template or render function not defined.
I can do the following:
import Default from "./default/Default.vue";
import Type2 from "./type2/Type2.vue";
let menu = Default;
if (process.env.VUE_APP_MENU_TYPE === "2") {
menu = Type2;
}
export default menu;
This will work but all menus are compiled in the code, including menus that will never be used since VUE_APP_MENU_TYPE is known at compile time and will never change until you recompile.
Is it possible to import a component dynamically at compile time?
Try menu = require("./type2/Type2.vue").default;
Explanation taken from this answer
when dealing with ES6 imports (export default MyComponent), the exported module is of the format {"default" : MyComponent}. The import statement correctly handles this assignment for you, however, you have to do the require("./mycomponent").default conversion yourself. If you want to avoid that, use module.exports instead of export default
Fort second part of question...
Is it possible to import a component dynamically at compile time?
Really never tried but I have my doubts. Webpack is not executing the code when building. It just scans it for some patterns.
It scans for require() so it know what modules should be included in the bundle
DefinePlugin is replacing strings like process.env.VUE_APP_MENU_TYPE with values from env files so it make code look like if ("3" === "2") {
Other plugins are able to detect that if ("3" === "2") { is never true and eliminate "the death code"
Real question if what happens first - if require scanning happens before death code elimination, you will end up with all possible menu components in the bundle. But unfortunately I don't know - You'l have to try
On the other hand using dynamic async components (as mentioned in other answers) is sure bet. Yes, Webpack will build all possible menu components but each with it's own js chunk (js file in dist folder). If the app loads just one of them, it's seems fine to me (note that the loading will be async - at runtime - so it means one more server request)
I think that loading the component dynamically is the best option you have.
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
I could have solved this with a webpack setting.
In vue.config.js
const path = require("path");
module.exports = {
configureWebpack: {
resolve: {
alias: {
MENU: path.resolve(
__dirname,
(() => {
if (process.env.VUE_APP_MENU_TYPE === "2") {
return "src/components/header/CategoriesMenu/Type2/Type2.vue";
}
return "src/components/header/CategoriesMenu/Default/Default.vue";
})()
),
},
},
},
};
In src/components/header/CategoriesMenu/index.js
import menu from "MENU";
export default menu;
But to be honest, I like the require better.
tl:dr;
class ModuleInBundleA extends ModuleInBundleC { … }
window.moduleInBundleB.foo(new ModuleInBundleA())
class ModuleInBundleB {
public foo(bar: ModuleInBundleA|ModuleInBundleC|number) {
if (bar instanceof ModuleInBundleA || bar instanceof ModuleInBundleC) {
// always false
…
}
}
}
Details:
I'm trying to start using TypeScript + Webpack 4.41.6 on the project that has mostly old codebase. Basically I want to package several small modules onto bundles to migrate softly without moving whole project onto new js stack.
I found out that Webpack can do this with code splitting, and package shared code into bundles on it's own with some configuration. However I can't really control what will be in every bundle unless I build every bundle separately and then only share types, using my own modules as external libraries and that's bit frustrating.
Maybe on this point you can say that I'm doing something wrong already and I would like to hear how can I achieve my goal of using bundles just as vanilla javascript (controlling defer/async on my own and using script tag on my own as well), and I don't really want to pack everything as an independent package with own configuration, types export and so on.
Hope you got overall context. Closer to the point.
I have the following function, that is bundled to it's own chunk called modal-manager.js.
public showModal (modal: ModalFilter|AbstractModal|number) {
let modalId: number;
console.log(modal);
console.log(typeof modal);
console.log(modal instanceof ModalFilter);
console.log(modal instanceof AbstractModal);
if (modal instanceof AbstractModal) {
modalId = modal.getId();
} else {
modalId = modal;
}
...
};
(Originally it had no ModalFilter as ModalFilter inherits AbstractModal but I included it for demonstration purposes)
The abstract modal is bundled automatically to modal-utils.js as it's shared between modules.
Next, I have another big bundle called filter.js. This one literally creates instance of ModalFilter const modalFilter = new ModalFilter(...). I think it's work mentioning that instance of modalFilter declared to the global window variable. The trouble is that filter.js calls modal.js code (through window.modalFilter.showModal(modalFilter)) with no problems whatsoever, but I see the following result of console.log:
ModalFilter {shown: false, loading: false, closing: false, html: init(1), id: 0, …}
modal.bundle.23e2a2cb.js:264 object
modal.bundle.23e2a2cb.js:265 false
modal.bundle.23e2a2cb.js:266 false
I disabled mapping to get more into code and see this:
ModalManager.prototype.showModal = function (modal) {
var modalId;
console.log(modal);
console.log(typeof modal);
console.log(modal instanceof _builder_component_modal_filter__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"]);
console.log(modal instanceof _modal_abstract__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"]);
if (modal instanceof _modal_abstract__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"]) {
modalId = modal.getId();
}
else {
modalId = modal;
}
this.modals[modalId].show();
this.scrollLock(modalId);
};
With my understanding of how javascript works, instanceof should check the object-creator function. As code chunks separated (modal.js has no same code with modal-utils.js) the creator function should be the same. However, getting more to the details I see that webpackJsonp can be really tricky and calling them from kind-of independent environments, still it should be the same environment where FilterModal, AbstractModal is called. The ModalManager could have own environment I believe. But code called is 100% the same. Could that webpackJsonp bundle-arrays be the source of the problem? If so, how can I avoid that and make modal.js bundle understand that both filter.js and others reference the same AbstractModal from modal-utils.js?
If I'm doing it wrong, is there a simple way to start bundling small and efficient scripts build with TypeScript and Webpack (or other tools)?
Also, I see the externals feature of Webpack, but haven't figured out how to use that in my case. In general, I'm ok with current set up except instanceof issue. The reason I want to avoid multiple builds is that I'll probably have dozens of smaller bundles that shared across different modules and having dozen of npm packages for each seems excessive.
Prefacing this with; I don't know the answer to the exact issue that you are facing in regards to the instanceOf part of your question. This is aimed at the "how did you do it" part.
Approx. 4 weeks ago we also changed from a .js to .ts implementation with about 1-2 hunderd .js files. Obviously we didn't want to migrate these all at once over to .ts as the effort was too high.
What we ended up doing was identifying .js scripts which needed to run on specific pages and added these into webpack as entry files. Then for all of the other suporting scripts, if we required their contents in our new .ts files, we actually created a large index/barrel file for them all, imported them in and then webpack will automatically include these in the correct scope alongside their respective .ts files.
What does this look like?
legacy.index.ts: For every single supporting .js file that we wanted to reference in any way in .ts.
var someFile_js = require("someFile.js");
export { someFile_js };
This then allowed us to import and use this in the .ts files:
import { someFile_js } from './legacy.index';
In reply to #tonix. To load a defined list:
webpack.config
const SITE_INDEX = require('./path-to-js-file/list.js')
module.exports = {
entry: SITE_INDEX
...
}
list.js
{
"filename1": "./some-path/filename1.js"
"filename2": "./some-path/filename2.ts"
}
I have a different homepage in all my environment. One for staging, one for development. The issue is in each environment, I need only 1 homepage and not the other one.
Actually I have a temporary solution : with a 'if' I test and it's load the good one but I think that's not a good idea because I have to import all of my homepage when I am doing this.
All homepages are currently built when you import the files and there is some conflict with my CSS.
Maybe I need to change the webpack config, if you have some links to help me.
Or if you have some good practices to do it, that's sure be helpful !
import developmentHomePage from './developmentHomePage.jsx'
import stagingHomePage from './stagingHomePage.jsx'
const mapStateToProps = state => {
const currentEnvironnement = process.env.name
if (currentEnvironnement !== 'development') {
homePage = developmentHomePage
} else {
homePage = stagingHomePage
}
return {
homePage
}
}
you can fill homepage dynamically with environment variables. Check the given link to get the hint:
dynamically fill homepage