How to deploy a raw JSON file that Webpack 4 treats as a split module? - json

I don't think this is an uncommon problem, but it seems like the keywords turn up many false positives. I've tried searching for "webpack dynamic configuration file", "webpack runtime load JSON file", and more. I see many results for configuring Webpack dynamically, but not many for configuring a bundled app dynamically.
I want to have a configuration file that sits in my deployment as raw JSON, i.e. no Webpack runtime or module boilerplate. Just a valid JSON file.
I want to "import" that JSON configuration in my code as I would as if it were a module, i.e. like this:
import config from './config.json'
I want Webpack to omit the JSON file from the bundle, but insert any necessary code to asynchronously request and inject the config.json waiting on the server.
I want Webpack to ignore whether ./config.json exists at build time, and to just optimistically assume it will be in the right place at runtime.
I'd love if I could specify that './config.json' is a module alias, and for Webpack to copy the aliased file to the correct location (with name config.json) in the build directory.
This will give me a raw JSON file in my deployment that my site administrator can edit without running Webpack. It lets me as a developer code as if config.json is a regular module. How can I do this? I've seen suggestions to use
externals: {
'./config.json': "require('./config.prod.json')",
},
but that won't work in the browser, where require does not exist.
I've tried this configuration with no luck. The JSON is still inlined into the bundle:
resolve: {
alias: {
'./config.json': path.resolve(__dirname, 'src/config.prod.json')
}
},
optimization: {
splitChunks: {
cacheGroups: {
config: {
test: './config.json',
chunks: 'all',
name: 'config',
priority: 100
}
}
}
}
I am using Webpack 4.

In short, you can't use
import config from './config.json'
At least not if this should run in the browser.
webpack externals would make this possible for a nodejs application.
What you really want is a normal http call.
Just get the config.json file with something like fetch.
fetch('url/config.json')
You can use copy-webpack-plugin to put the config in the correct place when webpack compiles (but do you want that if there are changes directly to this file on the server)

Related

Grunt Uglify files from list in external document

I have a module system, which can be controlled via an admin UI, developers can enable or disable features and the task runner then takes care of preparing all UI assets into single files to load on the front-end.
With SASS, I can programmatically control the index.scss file to be compiled, meaning that this is simple enough - however, with JS I don't find the same option, yet.
The idea is to avoid any file duplication, manipulation or movement, to reduce complexity, avoid issues and also to speed up the task runner, which quickly gets bloated and slowed down.
As the process begins with a save routine, I can collect data about the current "active" modules and store this is any file format - json, csv, whatever - I would then like to load that config from the file in the Gruntfile - which might be continually watching ( in case it's a problem that config can only be loaded once? -- the data would need to be grabbed again fresh before each compilation ).
simple example:
*module.json
{"js":["modal.js","toast.js","tab.js","collapse.js","form.js","toggle.js","gallery.js","helper.js","scrollspy.js","scroll.js","lazy.js","javascript.js","comment.js","push.js","nprogress.js","consent.js","search.js","anspress.js","localize.js"]}
*Gruntfile.js
// ------- configuration ------- ##
module.exports = function(grunt) {
grunt.initConfig({
// load modules ##
modules: grunt.file.readJSON('module.json'),
'uglify': {
// options etc ##
files:{
'library/asset/js/module.min.js' : '<%= modules.js %>'
}
}
});
// Development Tasks ##
grunt.registerTask(
'default'
, [ 'uglify' ]
);
}
This config runs, but does not load the data from JSON correctly - Grunt says:
Destination library/asset/js/module.min.js not written because src
files were empty.
I know Grunt can read the JS, as I can do something like the following:
*terminal
$ grunt config
Running "config" task
["modal.js","toast.js","tab.js","collapse.js","form.js","toggle.js","gallery.js","helper.js","scrollspy.js","scroll.js","lazy.js","javascript.js","comment.js","push.js","nprogress.js","consent.js","search.js","anspress.js","localize.js"]
Done.
*Gruntfile.js
// load config ##
grunt.registerTask(
'config'
, function() {
modules = grunt.file.readJSON('module.json')
grunt.log.write(JSON.stringify( modules.js ) );
}
);
Any ideas of pointers? Thanks!
The answer, was pretty simple - to include full paths to each "file" in the JSON, then Grunt parsed it withuot problems.

Loading JSON files in React, without JSON extension

I'm trying to require some JSON files in my React app (based on CRA 3.01 with Typescript).
The normal const obj = require('./path/file.json') would work if my files had a .json extension - however, these files have .md for 'metadata' and a couple other extensions, and the standard require isn't working. The files are from a tool, so changing to .json isn't a practical option.
Doing some research, it seems the approach is to use the webpack json-loader module (the webpack json-loader docs says that working with different file extensions is the main reason for using the module). I found an example and am using this:
const context = require.context(
"json-loader!./metadata",
true,
/^\.\/.*\.md$/
);
const metadata = context("./foo.md");
I've got a minimum reproduction here (see App.tsx):
https://github.com/ericsolberg/testjson
It seems that this is correctly using the json-loader, and finding the file correctly. However, I'm getting a syntax error:
Error: Module build failed (from ./node_modules/json-loader/index.js):
SyntaxError: Unexpected token m in JSON at position 0
at JSON.parse (<anonymous>)
at Object.module.exports (/Users/***/projects/jsontest/node_modules/json-loader/index.js:4:49)
I did some research on this error, and believe the problem is that the file is being parsed twice - first by the loader configured by CreateCreactApp's default webpack config, then by the specified JSON loader.
I don't want to eject my CRA app to modify the webpack config, and would like to avoid a re-wire hack (and whatever other issues that introduces) ... does anyone know of a way to load JSON files in a CRA app, if these files don't have a JSON extension?
Here's the solution that ended up working for me.
I could eject my project, of course, and customize the webpack config to load JSON files with other extensions. It may be possible to make a rewire hack work as well.
But I realized that when I require a file that is not one of the extensions recognized by CRA's config, it instead copies that file into the build, and require('file.ext') returns the URL of the file. So I'm using axios to load the file. This means a trip to the server for something that could be done statically, but for where I'm taking this project that is actually OK (eventually it will load metadata from a server anyway).

Pre-compile YAML to JSON with webpack

On my page, I want to use i18next and Vue.js to display translated text. For that, I want to use YAML files for better maintainability. Here on Stackoverflow, I found this old question, where #steve-hynding posted a way to configure webpack to pre-compile the YAML files into JSON. However, the syntax he used (with the rule array) doesn't work in my case, because we're using chainWebpack. I tried to rewrite the rule, but it doesn't do anything at all.
chainWebpack: config => {
config.module
.rule('yaml')
.test(/.\.yaml$/)
.use('file-loader')
.loader('file-loader')
.options({
name: '[path][name].json',
context: 'src'
})
.end()
.use('yaml-loader')
.loader('yaml-loader')
.end();
}
How can I make webpack extract the *.yaml files from a specified folder, compile it into JSON and put it into a specified folder in the public dir?

Webpack compiling ES6 into module

I am trying to compile ES6 to a file using Webpack and can't figure out why the code is not usable as it is.
Side note : This is meant to be a plugin for VueJS
I start with a simple file that exports a single function such as
exports.install = () => {
...
}
Webpack uses babel-loader and babel-preset-es2015 to compile it.
You may find webpack config, source and compiled files in this gist.
My problem is the result is not "requirable" in my Vue app... It has some weird stuff around the core needed exports.install statement. When I remove all this stuff and leave just exports.install = ... it is OK, otherwise I just don't get anything out of it.
I am using it in another app built with webpack, through an import statement.
Without an output.libraryTarget option, webpack will generate a bundle you can include via a <script> tag, but not import. I think this is what you're seeing.
If you want to import (or require) the result of your webpack build, you should set libraryTarget to commonjs2
output: {
filename: 'index.js',
libraryTarget: "commonjs2"
},
With this libraryTarget configuration, the webpack output will look like module.exports = /* ... the "weird stuff" */, so when you import it, you'll get the exported function you expect.
If all you're doing is compiling a single file or set of files that will be imported in another webpack build, you might consider not using webpack at all, and instead using the Babel CLI directly. In your Gist, you're not getting anything from webpack other than wrapping your module in some extra webpack bootstrap code.

Exclude JSON files from r.js optimizer

I use a config.json file in my application to configure the application (big surprise) after deployment, pulling them in using the requirejs-text plugin. Ideally, I would like to keep this JSON file (among others) out of the optimized built file.
Here is my app hierarchy:
app/
data/
config.json
...
scripts/
main.js // require.config in here
controllers/
ctrl.js // Uses JSON files
My current build options (through gulp) for require.js are
{
baseUrl: 'app/scripts',
mainConfigFile: 'app/scripts/main.js',
name: 'main',
out: 'main.js'
}
Since these are just flat files I want to exclude and not modules, is there a way of keeping them out of the final file?
If you list it in the dependencies as 'text!config.json', you should be able to exclude it by listing it in exclude list as 'text!config.json'. I created a fake project to test it and it worked.
So:
{
baseUrl: 'app/scripts',
mainConfigFile: 'app/scripts/main.js',
name: 'main',
out: 'main.js',
exclude: ['text!config.json']
}