Dynamic configuration variables in Javascript / React - javascript

I am writing a Client / Server application with the front end UI based in React. As a back-end Unix developer web technologies are not my forte so this is all new to me.
I need to be able to configure the UI to point to the server's URL and also to set other preferences. The typical react approach seems to be to use .env environment variables. However, as per this link:
multiple-environments-with-react
'the “environment variables” will be baked in at build time'. This does not work for me as the application is an OEM offering to be sold to customers who would configure it themselves for their own environment. I do not know their server URLS at build time so I need a way that I can deliver the pre-built (minified / linted, etc) JS to them in a single archive and let them edit some sort of properties file to configure it for their needs.
What would the general JavaScript / React best practice be for this sort of use case?
Thanks,
Troy

The easiest solution for me turned out to be to include a tag in my index.html. This gets minified during the NPM build but it does not get bundled with the other javascript so it can easily be replaced with another file at deploy time. My config.js looks like this:
config = {
"title": "Application Title",
"envName": "LocalDev",
"URL": "localhost:8090"
}
Then inside my react components they're accessible by using:
const config = window.config;
alert("Application branding title is: " + config.title);
I will now have various config.js files for each environment (config.js.dev, config.js.uat, config.js.prod, etc) and at deployment I will link or renmae the appropriate one to config.js.

Related

How do I convert SmartyStreets' jQuery.LiveAddress plugin to its JavaScript SDK?

I have a website where the jQuery.LiveAddress plugin is implemented, but it was deprecated and then totally removed by SmartyStreets in 2014.
https://www.smartystreets.com/archive/jquery-plugin/website/configure
<script src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="//d79i1fxsrar4t.cloudfront.net/jquery.liveaddress/5.2/jquery.liveaddress.min.js"></script>
<script>var liveaddress = $.LiveAddress({
key: "YOUR_WEBSITE_KEY_HERE",
debug: true,
target: "US|INTERNATIONAL",
addresses: [{
address1: '#street-address',
locality: '#city',
administrative_area: '#state',
postal_code: '#zip',
country: '#country'
}]
});
</script>
There is now a JavaScript SDK: https://www.smartystreets.com/docs/sdk/javascript
const SmartyStreetsSDK = require("smartystreets-javascript-sdk");
const SmartyStreetsCore = SmartyStreetsSDK.core;
const Lookup = SmartyStreetsSDK.usStreet.Lookup;
// for Server-to-server requests, use this code:
// let authId = process.env.SMARTY_AUTH_ID;
// let authToken = process.env.SMARTY_AUTH_TOKEN;
// const credentials = new SmartyStreetsCore.StaticCredentials(authId, authToken);
// for client-side requests (browser/mobile), use this code:
let key = process.env.SMARTY_WEBSITE_KEY;
const credentials = new SmartyStreetsCore.SharedCredentials(key);
It says "for client-side requests (browser/mobile), use this code", but am I correct in assuming that it is not possible to get this code working as browser JavaScript? Wouldn't I need to be running node as a service on my server to be using this code?
The client-side code example can be used in the browser, not just in node. You will need to store your website key in a different way since the browser doesn't have access to process.env. You can store the website key in plaintext here since it will be tied to your hostname.
You will need to be using some kind of library or JS bundler to process the require statement you see on line 1. You can also import the SDK if that's what works better for your setup, for example
import SmartyStreetsSDK from "smartystreets-javascript-sdk"
Something to keep in mind with using the SDK vs the jQuery plugin is that the SDK does not provide and UI elements on the page. It's less of a drop in solution than the jQuery plugin is. You will need to design and create your own UI elements for the user to interact with.
disclaimer: I work for Smarty (formerly SmartyStreets)
I came across this same issue and unfortunately smarty streets decided to make it more difficult for users to implement their product.
Upon my search I came across this fiddle, it doesn't have all of the features the jquery plugin did but it is a good place to start that doesn't require building your own suggestion UI.
<script async src="//jsfiddle.net/smartystreets/uptgh7c8/embed/"></script>
jsfiddle source
The key file that I was missing was the browserify.js file in the SmartyStreets JavaScript SDK.
Run npm install smartystreets-javascript-sdk --save-dev in your project's root directory
Change directory to node_modules/smartystreets-javascript-sdk
Run npm install in this directory
Run rm -rf dist && node browserify.js (script will fail if the dist folder exists)
Copy dist/smartystreets-sdk-1...min.js to a dependencies folder for your main project.
I tried to write a grunt task to take the SmartyStreets SDK and automatically update it, browserify it, and deploy it along with the rest of our website, but it turned out to be too much of a hassle with the time constraints given. If anyone else writes a grunt task for this please share!

build react app and widget js in one webpack config

My task is to introduce library to existing create react app based application.
This library need to be build to separate chunk and should not contain any contenthash in name. Ideally should be build to buildDir/js/widget.js and that is.
Currently all my ts are compiled to js during build and are served with contenthash in name.
I don't know how to build widget.js from src/widget/index.ts because entry point is src/index.ts and it never catch src/widget/index.ts because it is not imported anywhere in main entry point.
This widget.js later will be imported in thirdparty web apps via <script> tag and it will be used to initialize some library like MyLibrary.init(...) so I think webpack should also have some info so this one widget.js should export its methods in special way to the browser during importing external script.
What is best way to get this build proces to work. Also it could be really nice to have it also during development with hot updates.
I don't want contenthash in resulting buildDir/js/widget.js because I don't want to ask my customers every time I have new version to update their <script src="..."> for new file name.
Should I eject this CRA? I'm not sure even if I add another entry point that I will be able to control output file name for one entry point as it is and for another without contenthash.
Or maybe it will be better to create separate webpack config (next to unejected CRA) for this widget but then how to run everything in development mode with hot updates?
I'm using webpack 4.42.0 here in this project.
for your case maybe you need this:
https://dev.to/zhiyueyi/include-your-react-widgets-in-any-web-page-emj
https://github.com/ZhiyueYi/demo-react-web-widget

Rails-like autoload in javascript - Allow dependency to require from root package in webpack

I am writing a framework package which I'd like to make it able to auto require modules from the main projects src/. If you are familiar with rails, this is akin to its autoload feature.
So if in your web app you follow a directory convention, say src/models/my-model.js, then the framework can require the my-model module on its own. The framework, which is a dependency of the web app, only need know the name of the relation (ie "todos") in order to require the model (ie. src/models/todo.js)
I've tried adding my web apps src directory in my web apps webpack chain config.resolve.modules.add(path.resolve(__dirname, 'src')) but it does not seem to apply to the search paths for dependencies (not sure) so my framework lib still can't find modules in my web app.
I've also (desperately) tried passing require from the web app to the dependency and then in the dependency code I call var MyModel = this.thePassedInRequireFn("./models/" + modelName), but it errors:
(`Uncaught Error: Cannot find module './models/my-model'
at MyFramework.webpackEmptyContext
Anybody have ideas how this can be done?
If the solution can be independent of the use of webpack, that would be ideal, but webpack compatibility is what is most important to me.
Here is a webpack specific answer using require.context().
https://webpack.js.org/guides/dependency-management/#require-context
In the web app create a require context. For example:
const requireModule = require.context('./models/', true);
Then pass requireModule to the framework you have as a dependency of your web app.
If your web app has a model in the file ./models/todo-item.js and the framework is given the models name todoItem, the framework can require it using only the model name like so:
let fileName = `./${kebabCase(modelName)}`;
let module = this.requireModule(fileName).default;

How to create an AngularCli application that contains another AngularCli inside?

I have an application in development that is in Angular2 using AngularCli, and I want to use it as a "Layout" (like a MasterPage) to another project. Like a big "SPA System".
For example, in the menu we will have the following:
Framework
Page A
Xpto
Page B
The Framework is running in http://localhost:90 and XPTO is running in http://localhost:91. Both of them is running on AngularCli.
I want to create a structure that when I click on Page A or Page B, the browser doesn't reload and it will give an "app" style to the user, loading the page as a SPA ACROSS the sites.
The main reason is to reuse the Javascripts, CSS and many other files from de "Framework" project to other 20 projects. I don't want to replicate all the components, files and etc across those projects.
Today we use MVC3 and the RazorGenerator to create .cshtml as a DLL to reuse the .cshtml from Framework to other modules.
But we want go AngularCli. Is there any way to do that ? If it isn't, is there some way to create a template in AngularCli that can be reused the components and the other files ?
Thank you !
You can put shared Angular components and modules into a separate npm package and use this package as a dependency for other projects.
In order to reference your npm package (I assume it's not hosted at npmjs.com) you can specify a git repository or local path.
Here is an example of package.json
{
"name": "foo",
"version": "0.0.0",
"dependencies": {
"my_git_package": "git+ssh://user#hostname/project.git#commit-ish",
"my_local_package": "file:../foo/bar"
}
}
Take a look at dependencies section here https://docs.npmjs.com/files/package.json

How can I read files from a subdirectory in a deployed Meteor app?

I am currently making a Meteor app and am having trouble reading files from the private subdirectory. I have been following a couple different tutorials and managed to get it to work flawlessly when I run the meteor app locally. This question (Find absolute base path of the project directory) helped me come up with using process.env.PWD to access the root directory, and from there I use .join() to access the private folder and the pertinent file inside. However, when I deployed this code, the website crashes on startup. I am very confident that it is an issue with process.env.PWD, so I am wondering what the proper method of getting Meteor's root directory on a deployed app is.
//code to run on server at startup
var path = Npm.require('path')
//I also tried using the below line (which was recommended in another Stackoverflow question) to no avail
//var meteor_root = Npm.require('fs').realpathSync( process.cwd() + '/../' );
var apnagent = Meteor.require("apnagent"),
agent = new apnagent.Agent();
agent.set('cert file', path.join(process.env.PWD, "private", "certificate-file.pem"))
agent.set('key file', path.join(process.env.PWD, "private", "devkey-file.pem"))
In development mode the file structure is different than after bundling, so you should never rely on it. Particularly, you should not access your files directly like you're doing with path methods.
Loading private assets is described in this section of Meteor's documentation. It mostly boils down to this method:
Assets.getBinary("certificate-file.pem");
and it's getText counterpart.
As for configuring APN agent, see this section of documentation. You don't have to configure the agent by passing file path as cert file param. Instead you may pass the raw data returned by Assets methods directly as cert. The same holds for key file ~ key pair and other settings.
As an alternative, you would need to submit your files independently to the production server to a different folder than your Meteor app and use their global path. This, however, would not be possible for cloud providers like Heroku, so it's better to use assets in the intended way.

Categories