Webpack, build one JSON file from multiple JSON files at build time - javascript

I have to build a landing page which when visited will check your User Agent and determine which device you are using and if you are using a device that is supported it will show some information for that device. The information that is shown does not matter that's done, checking User Agent is also done.
What is not done is this because I am stuck. I have directory structure like this:
-devices
-nintendo
config.json
-xbox
config.json
-ps5
config.json
and so on. So since I need to access these configs at runtime since I will not know which device the user is using until they land on the page, this cannot be determined at build time. So my thoughts are have a JS file that will loop through all of these folders and take each config and build one big config that will be a module in web pack that I can import in my main.js file so I can than just import it and access the properties using dot notation.
Can I do all of this with web pack and skip the JS file that will combine all of the config, or is there a better approach to this?

You don't really need anything extra to achieve this. Assuming you have following directory structure:
- main.js
- devices
- nintendo
- config.json
- xbox
- config.json
- ps5
- config.json
All you have to do is to import individual JSONs in your main.js file:
import nintendo from './devices/nintendo/config.json';
import xbox from './devices/xbox/config.json';
import ps5 from './devices/ps5/config.json';
function main() {
// Read `someDevice` at runtime
if (someDevice === 'nintendo') {
console.log(nintendo);
}
}
If your json files are big and bundle size becomes a concern, then you can use dynamic imports along with async-await and Webpack will automatically split the bundle for you:
async function main() {
if (someDevice === 'nintendo') {
const nintendo = await import('./devices/nintendo/config.json');
} else if (someDevice === 'xbox') {
const xbox = await import('./devices/nintendo/config.json');
} else {
const ps5 = await import('./devices/ps5/config.json');
}
}

Related

nodejs server - how to load file in memory to use in Modules

Good Morning Everyone,
First, I am not a full dev, this is a hobby, I'm using a NodeJS / ExpressJS server (in ES6) to serve an application I made for 2000 people. This was all reversed engineered. But I am in a rush to fix the app, as the normal API point I was using is now going down, I had to rewrite loads of things.
The last thing I need to fix most problems I have; I am using 2 JSON files that contains about 50MB data each. Obviously I don't want to load those everytime in memory.
The way I wrote my code, I'm using server.js to load all my API points that people can call.
Then I broke my functions withing Modules (files in different folders) to keep things clean.
Here are the questions.
Can I load those 2 files in Memory and access them from my Modules, if so how?
Removed my second question for update file - Will do it differently then with nodejs
As it was counter productive to try and make it work when I have another solution
Hi pSyToR if you can load your json files and then export them both. other files can then import them and edit the values on them if needed, you can also use globalThis to make them globally available
I have an example below of both
via exports
import * as fs from 'fs'
const fileA = JSON.parse(fs.readFileSync('fileA.json', { encoding: 'ascii' }))
const fileB = JSON.parse(fs.readFileSync('fileB.json', { encoding: 'ascii' }))
export { fileA, fileB }
Importing
import { fileA, fileB } from 'path/to/file-handler.js'
via globalThis
import * as fs from 'fs'
const fileA = JSON.parse(fs.readFileSync('fileA.json', { encoding: 'ascii' }))
const fileB = JSON.parse(fs.readFileSync('fileB.json', { encoding: 'ascii' }))
globalThis.fileA = fileA
globalThis.fileB = fileB
Importing
var data = globalThis.fileA.data
hope this helps.
You can use the following to delete the file from the memory management
delete require.cache[require.resolve('FILE')];

Include JSON files into React build

I know this question maybe exist in stack overflow but I didn't get any good answers, and I hope in 2020 there is better solution.
In my react app I have a config JSON file, it contains information like the title, languages to the website etc..
and this file is located in 'src' directory
{
"headers":{
"title":"chat ",
"keys":"chat,asd ,
"description":" website"
},
"languages":{
"ru":"russian",
"ar":"arabic",
"en":"English"
},
"defaultLanguage":"ru",
"colors":{
"mainColor":"red",
"primary":"green",
"chatBackGround":"white"
}
}
I want to make my website easy to edit after publishing it, but after I build my app, I can't find that settings.json file there in build directory.
I find out that files in public directory actually get included to build folder, I tried to put my settings.JSON in public,
but react won't let me import anything outside of src directory
I found other solutions like this one but didn't work
https://github.com/facebook/create-react-app/issues/5378
Also I tried to create in index.html a global var like (window.JSON_DATA={}), and attach a JS object to it and import it to App.js, but still didn't work.
How can I make a settings JSON file, and have the ability to edit it after publishing the app?
Add your settings.json to the public folder. React will copy the file to the root of build. Then load it with fetch where you need it to be used. For example if you need to load setting.json to the App.js then do the next:
function App() {
const [state, setState] = useState({settings: null});
useEffect(()=>{
fetch('settings.json').then(response => {
response.json().then(settings => {
// instead of setting state you can use it any other way
setState({settings: settings});
})
})
})
}
If you use class-components then do the same in componentDidMount:
class CustomComponent extends React.Component {
constructor(props) {
super(props);
this.state = {settings: null};
}
componentDidMount() {
fetch('settings.json').then(response => {
response.json().then(settings => {
this.setState({settings: settings});
})
})
}
}
Then you can use it in render (or any other places of your component):
function App() {
...
return (
{this.state.settings && this.state.settings.value}
)
}
The easiest way would be to require() the file on the server during server side rendering of the html page and then inline the json in the html payload in a global var like you mentioned window.JSON_DATA={}. Then in your js code you can just reference that global var instead of trying to use import.
Of course this approach would require you to restart your server every time you make a change to the json file, so that it get's picked up. If that is not an option then you'll need to make an api call on the server instead of using require().
You may want to look at using npm react-scripts (https://www.npmjs.com/package/react-scripts) to produce your react application and build. This will package will create a template that you can put your existing code into and then give you a pre-configure build option that you can modify if you would like. The pre-configured build option will package your .json files as well. Check out their getting started section (https://create-react-app.dev/docs/getting-started/)
If you don't want to go that route, and are just looking for quick fix, then I would suggest change your json files to a JS file, export the JS object and import it in the files you need it since you seem to be able to do that.
//src/sampledata.js
module.exports = {
sample: 'data'
}
//src/example.jsx (this can also be .js)
const sampledata = require('./sampledata');
console.log(sampledata.sample); // 'data'
you can use 'Fetch Data from a JSON File'
according to link
https://www.pluralsight.com/guides/fetch-data-from-a-json-file-in-a-react-app
example

Importing local HTML page with fetch into another HTML page

Basically, I have a html file called panel containing a simple DIV that I would like to insert into another main HTML file.
Instead of using web components, I'd like to implement a simple solution as described in this answer.
So, here is what I am doing for testing (just logging the panel to console):
panel.html
<div id="panel">
<h1>It works...</h1>
</div>
get-template.ts
export async function getTemplate(filepath: string, selectors: string) {
let response = await fetch(filepath);
let txt = await response.text();
let html = new DOMParser().parseFromString(txt, 'text/html');
return html.querySelector(selectors);
}
main.ts
import { getTemplate } from './get-template'
getTemplate('/path/to/panel.html','#panel').then((panel) => {console.log(panel);})
The console logs "null".
If this info could make any difference, I am using parcel-bundler to build the application.
The actual problem was determined by #CBroe and was about the fact that when parcel builds my application, the file path of my panel.html resource changes to be relative to the built dist folder.
Just to clarify:
before building the path is relative to the main.ts file
after building the path is relative to the dist folder
So the solution is to think about the final URL the panel.html will have, and refer to it in advance before building with parcel.
Something like this would work in my case:
main.ts (new)
import { getTemplate } from './get-template'
getTemplate('./panel.html','#panel').then((panel) => {console.log(panel);})
Then of course, the other step will be to copy the actual panel.hml file into the dist directory, otherwise the URL will point to a non existing file.
I see there was a github issue about automatically copy static (or assets) files in the parcel repository, and one of the solution provided is to use the plugin parcel-plugin-static-files-copy.

While developing a package for Atom, how to write to a JSON file inside the package folder?

I'm developing a package for the Atom text editor, and in order for that package to work, it reads some JSON files that act as a simple database. Also, those JSON files should be periodically updated with data obtained from a public spreadsheet, thus they must be written to.
The problem, however, is that using fs.writeFile, paths are relative to the user's home folder, not to the package folder. While I could use a path like .atom/packages/package-name/file.json, to my limited knowledge of Atom packages, that does not feel like a good practice (or is it?).
Are there any other solutions?
I don't see a problem using relative paths in your script like so:
const pathToJson = path.resolve(__dirname, 'file.json`);
// next: write data to JSON
However, if you really want to use absolute paths, there are several options.
Atom API
You can use the Atom API inside your package, which exposes the resolvePackagePath() method:
const packagePath = atom.packages.resolvePackagePath('name-of-your-package');
const pathToJson = path.resolve(packagePath, 'file.json`);
// next: write data to JSON
You can combine this with the following snippet to retrieve the package name from the manifest:
const { name } = require('./package.json');
3rd party package
The atom-read-manifest package allows you to do the same without having to specify the name of your package – but that's a question of personal taste, I guess.
const { readManifest } = require('atom-read-manifest');
// or use readManifestSync
(async () => {
const { name } = await readManifest();
const packagePath = atom.packages.resolvePackagePath(name);
const pathToJson = path.resolve(packagePath, 'file.json`);
// next: write data to JSON
})();
One final note: If your package is writing the data to the JSON, there are probably better ways to achieve the same. You need to consider that updating the package will overwrite your old file.json. Personally, I'd prefer to write the data to Atom's localStorage (or IndexedDB). If you prefer writing the data to a JSON file, then you should probably save it to .atom/storage instead.

How to import a module from the static using dynamic import of es6?

I'm trying to add dynamic import into my code to have a better performance on the client-side. So I have a webpack config where is bundling js files. On SFCC the bundled files are in the static folder where the path to that files is something like this: /en/v1569517927607/js/app.js)
I have a function where I'm using dynamic import of es6 to call a module when the user clicks on a button. The problem is that when we call for that module, the browser doesn't find it because the path is wrong.
/en/lazyLoad.js net::ERR_ABORTED 404 (Not Found)
This is normal because the file is on /en/v1569517927607/js/lazyLoad.js.
There is a way to get it from the right path? Here is my code.
window.onload = () => {
const lazyAlertBtn = document.querySelector("#lazyLoad");
lazyAlertBtn.addEventListener("click", () => {
import(/* webpackChunkName: "lazyLoad" */ '../modules/lazyLoad').then(module => {
module.lazyLoad();
});
});
};
I had the same problem and solved it using the Merchant Tools > SEO > Dynamic Mapping module in Business Manager.
There you can use a rule like the following to redirect the request to the static folder:
**/*.bundle.js i s,,,,,/js/{0}.bundle.js
All my chunk files are named with the <module>.bundle pattern.
Here you can find more info :
https://documentation.b2c.commercecloud.salesforce.com/DOC1/topic/com.demandware.dochelp/content/b2c_commerce/topics/search_engine_optimization/b2c_dynamic_mappings.html
Hope this helps.
I believe you'll likely need to do some path.resolve() magic in either your import statement or your webpack.config.js file as is shown in the accepted answer to this question: Set correct path to lazy-load component using Webpack - ES6
We did it in a different way. That required two steps
From within the template file add a script tag that creates a global variable for the static path. Something like
// inside .isml template
<script>
// help webpack know about the path of js scripts -> used for lazy loading
window.__staticPath__ = "${URLUtils.httpsStatic('/')}";
</script>
Then you need to instruct webpack to know where to find chunks by changing __webpack_public_path__ at runtime
// somewhere in your main .js file
// eslint-disable-next-line
__webpack_public_path__ = window.__staticPath__ + 'js/';
Optional step:
You might also want to remove code version from your __staticPath__ using replace (at least we had to do that)
__webpack_public_path__ = window.__staticPath__.replace('{YOUR_CODE_VERSION_GOES_HERE}', '') + 'js/';

Categories

Resources