In a firebase functions project I want to have some settings in a settings.json and then import the settings in my typescript files. For some reason, the import doesn't seem to succeed and I can't see or figure out why.
My folder/file structure looks like this:
src
- database
- index.ts <-- only exports uppercase.ts
- uppercase.ts
- regions.ts
In uppercase.ts I import regions.ts to use the functions object with the configuration to deploy on europe-west1.
import { functionsEUWest1 } from '../regions';
export const uppercase = functionsEUWest1.database.ref('/messages/{pushId}/original').onCreate((snapshot, context) => {
//Code as can be found in the tutorials of firebase.
});
In the regions, I try to read the settings from settings.json to use for the configuration.
import functions from 'firebase-functions';
import settings from "./settings.json";
const region: any = settings.region;
export const functionsEUWest1 = functions.region(region);
And this is the content of settings.json
{
"region": "europe-west1"
}
The folder/file structure of the build output is the same (only in lib instead of src but this is by default configured by firebase).
When I try to deploy my functions, I get the following error:
Error: Error occurred while parsing your function triggers.
TypeError: Cannot read property 'region' of undefined
at Object.<anonymous> (<local basepath>\functions\lib\regions.js:9:57)
at Module._compile (module.js:653:30)
at Object.Module._extensions..js (module.js:664:10)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)
at Function.Module._load (module.js:498:3)
at Module.require (module.js:597:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (<local basepath>\functions\lib\database\uppercase.js:3:19)
at Module._compile (module.js:653:30)
When I look into the build output in the file regions.js, I can't detect any rarity of transpiled code:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const firebase_functions_1 = __importDefault(require("firebase-functions"));
const settings_json_1 = __importDefault(require("./settings.json"));
const region = settings_json_1.default.region;
exports.functionsEUWest1 = firebase_functions_1.default.region(region);
//# sourceMappingURL=regions.js.map
Also the settings.json is copied to the build location and is in the same folder as regions.js. Yet the property default from setting_json_1 is undefined and I can't figure out what goes wrong.
Edit
I added 2 settings to the tsconfig.json. I did this cause it was advised in several articles I found:
"resolveJsonModule": true,
"esModuleInterop": true
After some more research I came up later on, I figured out it wasn't the json import that didn't seem to work, but it looks like the setting
"esModuleInterop": true
doesn't seem to work together with firebase or perhaps more specific: firebase functions, for so far as I can see and conclude.
The research to the conclusion
First I wanted to figure out if import in TypeScript does work at all, so I created 3 .ts files and one .json file and one of the .ts files I put in a folder. Then I generated the tsconfig.json with
tsc --init
and I added the 2 settings (resolveJsonModule and esModuleInterop). I transpiled the .ts files to .js files and ran the code with node. And this works, I saw the setting value from the .json file printed in the console.
The .ts file that imported the .json file has these lines of code:
import settings from "./settings.json";
console.log('from test.ts: ' + settings.setting1);
export const Settings = settings;
This got transpiled to:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var settings_json_1 = __importDefault(require("./settings.json"));
console.log('from test.ts: ' + settings_json_1.default.setting1);
exports.Settings = settings_json_1.default;
Now, I don't understand this code completely because I don't know what __importDefault or mod are.
But this code along with the other code does do what it suppose to do: read the setting from the .json file and print it in the console. So importing .json files does work. So it got to do something with firebase.
The next step I did, was creating a clean firebase project which uses functions and hosting. In the by the firebase CLI genrated index.ts in the functions/src folder, I changed the code to this:
import functions from 'firebase-functions';
import settings from "./settings.json";
const setting1 = settings.setting1;
export const helloWorld = functions.region('europe-west1').https.onRequest((request, response) => {
response.send("Hello from Firebase! Settings1 value = " + setting1);
});
And I also added the same 2 settings to tsconfig.json (resolveJsonModule and esModuleInterop). When I try to deploy this function to the firebase cloud, I got an error that is the same as I mentioned in my Question post:
TypeError: Cannot read property 'region' of undefined
But this time, I didn't had a property region in my .json file. So the fact I had region in my code in the question post and the fact I didn't look at the line numbers of the stacktrace, mislead me, making me think the .json import didn't work. But it is working.
The cause of the error is the method region that is called on the firebase_functions_1.default. For some reason, default is undefined and that is generating the error. This made me also realise I really miss the name of the object or objects in the error. What I would like to see is something like
TypeError: Cannot read property 'region' of undefined (firebase_functions_1.default)
So, back to the problem, I still got the error, but it wasn't cause by the .json import. To figure out what the actual cause was, I first reverted the "esModuleInterop" setting in the tsconfig.json, which results in errors in the import section in my index.ts. I had to change the imports back to
import * as functions from 'firebase-functions';
import * as settings from "./settings.json";
And now, the deploy to the firebase cloud works again, with importing a .json file. Also, the transpiled code looks different without using the "esModuleInterop" setting:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const functions = require("firebase-functions");
const settings = require("./settings.json");
const setting1 = settings.setting1;
exports.helloWorld = functions.region('europe-west1').https.onRequest((request, response) => {
response.send("Hello from Firebase! Settings1 value = " + setting1);
});
//# sourceMappingURL=index.js.map
It no longer has the default property on firebase_functions_1 but instead now just has functions.
It depends on some of your build and tsconfig settings. I've seen the above working for some people, where it uses the default export. But for me (Webpack with json-loader) this is what I use to import JSON files:
import * as settings from "./settings.json";
And of course, in my case, I add a definition like so to my global.d.ts file:
declare module "*.json";
Just so TS won't complain about it.
Related
I'm trying to read my i18n-strings from a JSON file following this guide.
I have en-US.json:
{
"world": "the world!"
}
And for setting up my Vue app I use:
import { createI18n } from 'vue-i18n'
import enUS from '../src/i18n/en-US.json'
// Type-define 'en-US' as the master schema for the resource
type MessageSchema = typeof enUS
const i18n = createI18n<[MessageSchema], 'en-US'>({
locale: 'en-US',
messages: {
'en-US': enUS
}
})
This works. But as soon as I add one non-ASCII char (e.g. "world": "the w#rld!"), I get the following error message:
[plugin:vite-plugin-vue-i18n] Cannot read properties of undefined (reading 'message')
/home/bernhard/wd/quasar2-project/src/i18n/en-US.json
Strangely, this works when I do the following straight in my .ts file:
const enUS = {
"world": "the w#rld!"
}
so maybe something wrong with the way the JSON is processed?
The answer has two layers.
As #jonrsharpe pointed out, there is a set of special characters: { } # $ | - i.e. for inserting variables. A # should i.e. be replaced by {'#'}.
A bug in how vite processes external json as described on this bug on Github. By default, quasar is using "#intlify/vite-plugin-vue-i18n": "^3.3.1". Changing that to "#intlify/vite-plugin-vue-i18n": "^7.0.0" solved the issue.
I just want to do a simple thing: import a JSON file and read it within a function to return some specific data of it, but it's been really hard to do.
When on dev env, it works perfectly, but when I try to do the build, the file is not fount. It's like it's not being imported. Since I'm new with Next.JS architecture, I'm not able to find a solution by myself (read a lot of pages with help, but no success).
structure
- root
- config
- labels
data.json
index.ts
index.ts
import data from './data.json';
export type HomeLabels = typeof data.in.home;
export function getHomeLabels(language: string): HomeLabels {
return data[language as keyof typeof data].home;
}
data.json
{
"in":{
"home":{
"contact_button": "Contact",
"view_all_button": "View all"
}
},
"pt-br":{
"home":{
"contact_button": "Contato",
"view_all_button": "Ver tudo"
}
}
}
error on build
TypeError: Cannot read properties of undefined (reading 'home')
at getHomeLabels (/home/runner/work/boutme/boutme/.next/server/chunks/261.js:42:43)
All the solutions that I found out was related to using API or getStaticsProps and using on component. But I really don't want it. I think that it's possible to do what I want (import json and read just on a function), but it's been hard.
Here is my code:
import { Map as LeafletMap, TileLayer } from 'react-leaflet';
function Map() {
return (
<div className="map">
<LeafletMap>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'/>
</LeafletMap>
</div>
But I got an error saying Attempted import error: 'Map' is not exported from 'react-leaflet' (imported as 'LeafletMap'). I've tried changing the 'import { Map' to 'import { MapContainer' but still won't work. Instead I got another error saying:
./node_modules/#react-leaflet/core/esm/path.js 10:41
Module parse failed: Unexpected token (10:41)
File was processed with these loaders:
* ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
| useEffect(function updatePathOptions() {
| if (props.pathOptions !== optionsRef.current) {
> const options = props.pathOptions ?? {};
| element.instance.setStyle(options);
| optionsRef.current = options;
I also tried changing the react-leaflet version from ^3.2.1 to ^2.7.0 and leaflet from ^1.7.1 to ^1.6.0. But still no luck. Any solutions?
You'll have to explicitly transpile react-leaflet because the maintainer is not understanding they should change their transpilation target to a lower version in order to more fully support the nullish coalescing operator: https://github.com/PaulLeCam/react-leaflet/issues/877#issuecomment-841871904
You can try adding this to your babel config
{
"plugins": ["#babel/plugin-proposal-nullish-coalescing-operator"]
}
And then make sure you transpile node_modules/react-leaflet during your build. If you are already using #babel/preset-env then you only need to make sure you're transpiling react-leaflet during the build. If you're using webpack you can do something like
module: {
rules: [
{
test: /\.jsx?$/,
exclude: filename => {
return /node_modules/.test(filename) && !/react-leaflet/.test(filename)
},
use: ['babel-loader']
}
]
}
after some google searched,
I am doing this in plugins/callApi.js
import axios from 'axios';
import Vue from 'vue';
Vue.use(callApi);
export default async (apiUrl, postData, headers = {}) => {
let msg = await axios.post(https://my-api-location.com' + apiUrl,postData);
return msg.data;
};
then set in nuxt.config.js
plugins: [
'~plugins/callApi.js']
when I want to use it
const msgData = await callApi('/api/news/getNewsList', postData);
if (msgData.response === 'success') { .......
but when I start yarn dev, it hightlight "Vue.use(callApi);" part and says "callApi is not defined"
how cant I fix it? thanks a lot :)
I actually re-read your answer because I was a little confused.
So, in your callApi.js, you define what you want there, but you are calling Vue.use(callApi) in the file where you are actually defining what callApi will be.
At the moment the compiler goes to Vue.use(callApi), this variable "callApi" is not defined yet (because it will only be available after it finishes compiling this very file).
So just do the
import axios from 'axios';
import Vue from 'vue';
export default async (apiUrl, postData, headers = {}) => {
let msg = await axios.post(https://my-api-location.com' + apiUrl,postData);
return msg.data;
};
then, by setting the plugin path on the plugins property in the nuxt.config file (exactly the way you did), the very callApi file will be automatically called (try to put a console log in your callApi file, and you'll see it logging when you start your application).
An example of what you want:
https://codesandbox.io/s/nuxt-app-demovue-ssr-forked-s94rz?file=/pages/index.vue
Now in your plugin, you have to decide exactly what you want to do. you might want to expose the function or make it globally available, that's up to you!
For this last option, you might want to take a look here:
Vue/Nuxt: How to define a global method accessible to all components?
Good luck!
This is a very simple import/export but the import is giving undefined no matter what. I've been following Mozilla's documentation. There's been a number of answers on Stackoverflow, and I've implemented several of the posted solutions, including making the export an object, and a function but nothing is working.
const env = "dev"
const content_dev = {
"artworks" : "http://127.0.0.1:8080/api/arts",
"projects" : "http://127.0.0.1:8080/api/projects",
"notes" : "http://127.0.0.1:8080/api/notes"
}
const content_prod = {
"artworks" : "https://bahlalala.com/api/arts",
"projects" : "https://bahlalala.com/api/projects",
"notes" : "https://bahlalala.com/api/notes"
}
const urls = (env == "dev") ? content_dev : content_prod
console.log(urls) // this is fine
export default urls
Where I'm doing the import.
import urls from "../../urlProvider"
console.log(urls) // undefined
Update: ok, so this works on the back-end but I need it to work in the browser. I have got babel and webpack for the SSR react app, but would module exports not work on the front end?