I have some JSON files in my Webpack application that I want to import. They have names such as 0.json, 1.json, 2.json, and so on and are inside of the directory src/res/level/. When I try to require() them in my code, it does not work:
private loadWorld() {
// load the level
// duplicate the object to avoid modifying the actual instance
// that json-loader created
this.state.level = JSON.parse(JSON.stringify(require(`#res/level/${this.level}.json`))) as LevelData;
// ...
}
This line in my method always throws an error:
Error: Cannot find module "#res/level/1.json".
at webpackContextResolve (webpack-internal:///9:16:11)
at webpackContext (webpack-internal:///9:9:11)
However, I cannot figure out why. And to make things more confusing, if I run Webpack in watch mode, and I edit this line before my program tries to run it, then the JSON files are suddenly loaded properly.
I have configured my alias for #res properly:
resolve: {
extensions: [".ts", ".js", ".glsl", ".json"],
alias: {
"#res": path.join(__dirname, "src/res"),
"#lib": path.join(__dirname, "src/lib"),
"#shader": path.join(__dirname, "src/shader"),
"#control": path.join(__dirname, "src/control"),
"#scene": path.join(__dirname, "src/scene"),
"#util": path.join(__dirname, "src/util"),
}
}
And because this is Webpack 4, I simply did not include a loader for JSON.
So why is this not working?
Additionally, I notice that when I inspect the generated code, I see this:
Which suggests that the JSON files are being loaded, but not under the directory that I expect.
The compiler began to load the JSON files consistently when I used string concatenation instead of a template string:
this.state.level = JSON.parse(JSON.stringify(require("#res/level/" + this.level.toString() + ".json"))) as LevelData;
I must be making a mistake somewhere, but it's not being written to stdout during optimization. I'm trying to optimize a file via requirejs, but the output isn't being minified. According to the documentation, UglifyJS should minify the code.
At any rate, the following code is trivial, but it isolates the problem.
src/index.js:
require(['config'], function () {
require(['myMod'], function (myMod) {
console.log(myMod.x());
})
})
src/myMod.js:
define(function () {
let myMod = {
x: 5
};
return myMod;
})
src/config.js:
define(function () {
require.config({
baseUrl: 'src'
});
})
And here's the gulp task that is performing the optimization:
gulp.task('optimize', function (cb) {
let config = {
appDir: 'src',
dir: 'dist/src',
generateSourceMaps: true,
preserveLicenseComments: false,
removeCombined: true,
baseUrl: './',
modules: [{
name: 'index',
include: ['myMod']
}]
}
let success = function (buildResponse) { console.log(buildResponse); cb() },
error = function (err) { console.log(err); cb(err) }
rjs.optimize(config, success, error)
})
After running the task, dist/src/index.js has all of the other modules included in it. However, it's not minified, and none of the variables have been renamed. Instead, it's as if the files were just concatenated, nothing more. Could someone tell me (1) why is it not being minified? (2) is UglifyJS throwing an error? If so, is there a way to see it when the gulp task is being run?
EDIT Here's a link to RequireJS docs where it talks about using the optimizer in node, which is done in the gulp task mentioned above. It's at the bottom under "Using the optimizer as a node module".
http://requirejs.org/docs/node.html
RequireJS' optimizer bundles UglifyJS2. UglifyJS2 does not handle ES6 or higher. If I take the options you use in your gulpfile, and plunk them into a separate file that I name options.js, and issue this command:
$ ./node_modules/.bin/r.js -o options.js
Then I get this output:
Tracing dependencies for: index
Uglify file: /tmp/t33/dist/src/index.js
Error: Cannot uglify file: /tmp/t33/dist/src/index.js. Skipping it. Error is:
SyntaxError: Unexpected token: name (myMod)
If the source uses ES2015 or later syntax, please pass "optimize: 'none'" to r.js and use an ES2015+ compatible minifier after running r.js. The included UglifyJS only understands ES5 or earlier syntax.
index.js
----------------
config.js
index.js
myMod.js
As you can see, UglifyJS does fail to minify your file, and RequireJS just skips the minification step for that file. Since this is not an outright error, the file is still output, just not minified.
If you change let to var in myMod.js, then the issue disappears.
Unfortunately, since this is not an execution failure (r.js still runs, it just does not minify the file), the error is not signaled to the errback handler you pass to rjs.optimize. I don't see a way to catch such error in a Gulpfile. The safe thing to do is to set optimize: "none" and perform the minification as an additional build step after running rjs.optimize.
I had also run into the same issue where require.js's optimizer (r.js) was combining different modules, but, it was not minify-ing the merged file. Although my run time environment is different from yours (using Java's Nashorn engine), this error was visible on my console :
If the source uses ES2015 or later syntax, please pass "optimize: 'none'" to r.js and use an ES2015+ compatible minifier after running r.js. The included UglifyJS only understands ES5 or earlier syntax.
Also, this error does not stop the optimizer from combining the files, it's just that the optimizer will not be able to mini-fy the merged file.
I'm working with cucumber and protractor and to generate reports i'm using
cucumber-html-reporter I already add the configuration to generate the report
var options = {
theme: 'bootstrap',
jsonFile: 'reporter/cucumber_report.json',
output: 'reporter/cucumber_report.html',
reportSuiteAsScenarios: true,
launchReport: true,
};
defineSupportCode(function({ After }) {
After((scenario)=> {
reporter.generate(options);
});
});
but i'm not generate the json file with this code, I search in google and the code to generate the json file should be add into the cucumberOpts in the conf.js but i'm not sure what's the code should be into cucumberOpts to generate the json file and the convert into report.
I hope you can help me guys.
Maybe this can help you, it's for Typescript but the code is almost the same. You can export the file in an After-hook. The link is for CucumberJS 1, if you look into the master branch you can also find the CucumberJS 2 solution
The advantage of this in comparison to generating the JSON file with the format option is that you can modify the JSON before saving
Hope it helps
For people that still have this problem, in my case the problem was that I was using cucumber v1 instead cucumber v2. For this case I should use registerHandler instead After this is the complete example:
defineSupportCode(function({registerHandler}) {
registerHandler('AfterFeatures', function (features) {
reporter.generate(options);
});
});
Hope this help u guys.
How can I access a JSON file in ECMAScript 6?
The following doesn't work:
import config from '../config.json'
This works fine if I try to import a JavaScript file.
https://www.stefanjudis.com/snippets/how-to-import-json-files-in-es-modules-node-js/
ES modules are still reasonably new in Node.js land (they're stable since Node 14). Modules come with a built-in module system, and features such as top-level await.
I read an informative post on ES modules by Pawel Grzybek and learned that you can't import JSON files in ES modules today.
import info from `./package.json` assert { type: `json` };
const { default: info } = await import("./package.json", {
assert: {
type: "json",
},
});
That's a real bummer because I'm pretty used to doing require calls such as const data = require('./some-file.json') in Node.js.
But can you use import assertions in Node.js today?
At the time of writing, the current Node.js LTS (v18.12) still marks import assertions as experimental.
This post explains ways to deal with JSON in ES modules if you don't want to use the experimental feature yet.
Option 1: Read and parse JSON files yourself
The Node.js documentation advises to use the fs module and do the work of reading the files and parsing it yourself.
import { readFile } from 'fs/promises';
const json = JSON.parse(
await readFile(
new URL('./some-file.json', import.meta.url)
)
);
Option 2: Leverage the CommonJS require function to load JSON files
The documentation also states that you can use createRequire to load JSON files. This approach is the way Pawel advises in his blog post.
createRequire allows you to construct a CommonJS require function to use typical CommonJS features such as reading JSON in your Node.js EcmaScript modules.
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const data = require("./data.json");
In TypeScript or using Babel, you can import json file in your code.
// Babel
import * as data from './example.json';
const word = data.name;
console.log(word); // output 'testing'
Reference:
https://hackernoon.com/import-json-into-typescript-8d465beded79
Importing JSON using ES modules was submitted as feature to TC39 in mid 2020, and is (at the time of this edit) in stage 3, which is the last stage before being accepted in to the spec (see https://github.com/tc39/proposal-json-modules for more details). Once landed, you will be able to use it as:
import someName from "./some/path/to/your/file.json";
Where someName is effectively the name of the variable for the JS object described by the JSON data. (And of course, note that this imports JavaScript from a JSON source, it does not "import JSON").
If you're using a modern enough bundler (like esbuild or the like) or you're using a recent enough transpiler (like babel) then you can already use this syntax without having to worry about support.
Alternatively, if you have the luxury of ownership of JSON files you can also turn your JSON into valid JS files with a minimum of extra code:
config.js
export default
{
// my json here...
}
then...
import config from '../config.js'
does not allow import of existing .json files, but does a job.
Unfortunately, ES6/ES2015 doesn't support loading JSON via the module import syntax. But...
There are many ways you can do it. Depending on your needs, you can either look into how to read files in JavaScript (window.FileReader could be an option if you're running in the browser) or use some other loaders as described in other questions (assuming you are using NodeJS).
IMO simplest way is probably to just put the JSON as a JS object into an ES6 module and export it. That way, you can just import it where you need it.
Also, worth noting if you're using Webpack, importing of JSON files will work by default (since webpack >= v2.0.0).
import config from '../config.json';
If you're using node you can:
const fs = require('fs');
const { config } = JSON.parse(fs.readFileSync('../config.json'));
OR
const evaluation = require('../config.json');
// evaluation will then contain all props, so evaluation.config
// or you could use:
const { config } = require('../config.json');
Else:
// config.js
{
// json object here
}
// script.js
import { config } from '../config.js';
OR
import * from '../config.json'
I'm using babel+browserify and I have a JSON file in a directory ./i18n/locale-en.json with translations namespace (to be used with ngTranslate).
Without having to export anything from the JSON file (which btw is not possible), I could make a default import of its content with this syntax:
import translationsJSON from './i18n/locale-en';
Depending on your build tooling and the data structure within the JSON file, it may require importing the default.
import { default as config } from '../config.json';
e.g. usage within Next.js
In a browser with fetch (basically all of them now):
At the moment, we can't import files with a JSON mime type, only files with a JavaScript mime type. It might be a feature added in the future (official discussion).
fetch('./file.json')
.then(response => response.json())
.then(obj => console.log(obj))
In Node.js v13.2+:
It currently requires the --experimental-json-modules flag, otherwise it isn't supported by default.
Try running
node --input-type module --experimental-json-modules --eval "import obj from './file.json'; console.log(obj)"
and see the obj content outputted to console.
Thanks to all the people who proposed and implemented JSON modules and Import Assertions. Since Chrome 91, you can import JSON directly, for example:
// test.json
{
"hello": "world"
}
// Static Import
import json from "./test.json" assert { type: "json" };
console.log(json.hello);
// Dynamic Import
const { default: json } = await import("./test.json", { assert: { type: "json" } });
console.log(json.hello);
// Dynamic Import
import("./test.json", { assert: { type: "json" } })
.then(module => console.log(module.default.hello));
Note: other browsers may not yet implement this feature at the moment.
A bit late, but I just stumbled across the same problem while trying to provide analytics for my web app that involved sending app version based on the package.json version.
Configuration is as follows: React + Redux, Webpack 3.5.6
The json-loader isn't doing much since Webpack 2+, so after some fiddling with it, I ended up removing it.
The solution that actually worked for me, was simply using fetch.
While this will most probably enforce some code changes to adapt to the async approach, it worked perfectly, especially given the fact that fetch will offer json decoding on the fly.
So here it is:
fetch('../../package.json')
.then(resp => resp.json())
.then((packageJson) => {
console.log(packageJson.version);
});
Do keep in mind, that since we're talking about package.json specifically here, the file will not usually come bundled in your production build (or even dev for that matter), so you will have to use the CopyWebpackPlugin to have access to it when using fetch.
Simply do this:
import * as importedConfig from '../config.json';
Then use it like the following:
const config = importedConfig.default;
Adding to the other answers, in Node.js it is possible to use require to read JSON files even inside ES modules. I found this to be especially useful when reading files inside other packages, because it takes advantage of Node's own module resolution strategy to locate the file.
require in an ES module must be first created with createRequire.
Here is a complete example:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const packageJson = require('typescript/package.json');
console.log(`You have TypeScript version ${packageJson.version} installed.`);
In a project with TypeScript installed, the code above will read and print the TypeScript version number from package.json.
For NodeJS v12 and above, --experimental-json-modules would do the trick, without any help from babel.
https://nodejs.org/docs/latest-v14.x/api/esm.html#esm_experimental_json_modules
But it is imported in commonjs form, so import { a, b } from 'c.json' is not yet supported.
But you can do:
import c from 'c.json';
const { a, b } = c;
import data from "./resource.json”
is possible in Chrome 91.
JSON modules are now supported. This allows developers to statically import JSON instead of relying on the fetch() function which dynamically retrieves it.
https://www.stefanjudis.com/snippets/how-to-import-json-files-in-es-modules/
A more elegant solution is to use the CommonJS require function
createRequire construct a CommonJS require function so that you can use typical CommonJS features such as reading JSON
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const data = require("./data.json");
importing JSON files are still experimental. It can be supported via the below flag.
--experimental-json-modules
otherwise you can load your JSON file relative to import.meta.url with fs directly:-
import { readFile } from 'fs/promises';
const config = JSON.parse(await readFile(new URL('../config.json', import.meta.url)));
you can also use module.createRequire()
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const config = require('../config.json');
I used it installing the plugin "babel-plugin-inline-json-import" and then in .balberc add the plugin.
Install plugin
npm install --save-dev babel-plugin-inline-json-import
Config plugin in babelrc
"plugin": [
"inline-json-import"
]
And this is the code where I use it
import es from './es.json'
import en from './en.json'
export const dictionary = { es, en }
I'm using
vuejs, version: 2.6.12
vuex, version: 3.6.0
vuex-i18n, version: 1.13.1.
My solution is:
messages.js:
import Vue from 'vue'
import Vuex from 'vuex';
import vuexI18n from 'vuex-i18n';
import translationsPl from './messages_pl'
import translationsEn from './messages_en'
Vue.use(Vuex);
export const messages = new Vuex.Store();
Vue.use(vuexI18n.plugin, messages);
Vue.i18n.add('en', translationsEn);
Vue.i18n.add('pl', translationsPl);
Vue.i18n.set('pl');
messages_pl.json:
{
"loadingSpinner.text:"Ładowanie..."
}
messages_en.json:
{
"loadingSpinner.default.text":"Loading..."
}
majn.js
import {messages} from './i18n/messages'
Vue.use(messages);
let filePath = '../../data/my-file.json'
let arrayImport = await import(filePath, {
assert: { type: "json" },
});
const inputArray = arrayImport.default
console.log('result', inputArray)
More information here: v8 - Dynamic import().
As said by Azad, the correct answer is to load the file with fs.readFileSync() (or any of the asynchronous variants such as fs.readFile with callback or fs.promises.readFile with promises/await, then parse the JSON with JSON.parse()
const packageJsonRaw = fs.readFileSync('location/to/package.json' )
const packageJson = JSON.parse(packageJsonRaw )
Webpack/Babel options are not practical unless you are already using that set up.
Make sure the type attribute is set to module because we are using the ES6 Modules syntax.
And here is how we would import a JSON file in our index.js file.
import myJson from './example.json' assert {type: 'json'};
import a JSON file in ECMAScript 6
import myJson from './example.json' assert {type: 'json'};
https://www.stefanjudis.com/snippets/how-to-import-json-files-in-es-modules-node-js/
ES modules are still reasonably new in Node.js land (they're stable since Node 14). Modules come with a built-in module system, and features such as top-level await.
I read an informative post on ES modules by Pawel Grzybek and learned that you can't import JSON files in ES modules today.
import info from `./package.json` assert { type: `json` };
const { default: info } = await import("./package.json", {
assert: {
type: "json",
},
});
That's a real bummer because I'm pretty used to doing require calls such as const data = require('./some-file.json') in Node.js.
But can you use import assertions in Node.js today?
At the time of writing, the current Node.js LTS (v18.12) still marks import assertions as experimental.
This post explains ways to deal with JSON in ES modules if you don't want to use the experimental feature yet.
Option 1: Read and parse JSON files yourself
The Node.js documentation advises to use the fs module and do the work of reading the files and parsing it yourself.
import { readFile } from 'fs/promises';
const json = JSON.parse(
await readFile(
new URL('./some-file.json', import.meta.url)
)
);
Option 2: Leverage the CommonJS require function to load JSON files
The documentation also states that you can use createRequire to load JSON files. This approach is the way Pawel advises in his blog post.
createRequire allows you to construct a CommonJS require function to use typical CommonJS features such as reading JSON in your Node.js EcmaScript modules.
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const data = require("./data.json");
The file structure with the json extension is used to transfer data, the json file data can be retrieved locally by sending a request using the fetch command.
In the following example, the data of the count.json file is received
// count.json
fetch("./count.json")
.then((response) => { return response.json(); })
.then((data) => console.log(data));
So first.. I have next gulp task:
gulp.task('js', function() {
browserify('./src/js/main.js')
.bundle()
.on('error', onError)
.pipe( source('main.js') )
.pipe( gulp.dest(path.build.js) );
});
and package.json:
{
"browserify": {
"transform": [
["babelify", { "presets": ["es2015"] }],
"debowerify"
]
},
}
I am importing Backbone in main.js (or only underscore... it doesn't matter)
import Backbone from 'backbone';
And in console I am getting error
Uncaught TypeError: Cannot read property '_' of undefined
I checked code and found that in underscore sources at start of library root is undefined
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
I think the problem is that debowerify or babelfy is wrapping code in some function. But also if I use node modules without debowerify all works fine. But I want to use bower.
So how to fix this problem?
To any future visitors to this question,
this is similar to Underscore gives error when bundling with Webpack
The gist of the issue is that babel is probably running the underscore.js code, since underscore.js uses this, and in es6 this is undefined when outside of a function, naturally this._ fails.
In code I've fixed the issue by ensuring that babel does not run on node_modules.
In my case the same error arose when using just browserify with underscore. I've workarounded issue by switching from underscore to lodash. They are in general (surely not fully) compatible, but at the worst I'd rather copy some missing function from underscore sources than live with its deisolated load approach.