The following snippet represents a Pinia store in my Vue 3 / Quasar 2 application. This store uses the environment variable VUE_APP_BACKEND_API_URL which shall be read from either the window object or process.env.
However I don't understand why the first variant is wokring but the second is not. Using the getEnv function always results in a Uncaught (in promise) ReferenceError: process is not defined error.
import { defineStore } from 'pinia';
function getEnv(name) {
return window?.appConfig?.[name] || process.env[name];
}
// 1. this is working
const backendApiUrl = window?.appConfig?.VUE_APP_BACKEND_API_URL || process.env.VUE_APP_BACKEND_API_URL;
// 2. this is NOT working
const backendApiUrl = getEnv('VUE_APP_BACKEND_API_URL');
export const useAppConfigStore = defineStore('appConfig', {
state: () => ({
authorizationUrl: new URL(
'/oauth2/authorization/keycloak',
backendApiUrl,
).toString(),
logoutUrl: new URL('/logout', backendApiUrl).toString(),
backendApiUrl: new URL(backendApiUrl).toString(),
}),
});
NodeJS-specific stuff like process doesn't exist in the browser environments. Both Webpack and Vite implementations work by replacing process.env.XYZ expressions with their values on build time. So, just process.env, or process.env[name] will not be replaced, which will lead to the errors you are experiencing. See the caveats section and related Webpack/Vite docs and resources. So, unfortunately, the only easy way seems to be the first long and repetitive way you've tried(const backendApiUrl = window?.appConfig?.VUE_APP_BACKEND_API_URL || process.env.VUE_APP_BACKEND_API_URL;). You can try embedding this logic in a single object, then use the function to access it.
const config = {
VUE_APP_BACKEND_API_URL: window?.appConfig?.VUE_APP_BACKEND_API_URL || process.env.VUE_APP_BACKEND_API_URL
}
export function getEnv(name) {
return config[name];
}
This way it will be longer and more repetitive to define it the first time, but at least you will be able to use it easily through the code base.
This is late, but it might help someone, I was able to resolve this by adding below to my quasar.conf.js
build: {
vueRouterMode: 'hash', // available values: 'hash', 'history'
env: {
API_ENDPOINT: process.env.API_ENDPOINT ? process.env.API_ENDPOINT : 'http://stg.....com',
API_ENDPOINT_PORT: process.env.API_ENDPOINT_PORT ? process.env.API_ENDPOINT_PORT : '0000',
...env
},
}
For more information ge here: https://github.com/quasarframework/quasar/discussions/9967
Related
I am trying to use scripts in illustrator. Some of these require being able to import other scripts so I found the below code. When I try to run it I receive
Error 21: Undefined is not an object.
Line 6 -> var Libraries = (function(libpath){"
I have looked through other answers and it seems the issue is that "Libraries" (?) is undefined, and that I ought to define it first. Sadly I don't know what it ought to be defined as. Or I don't understand the problem in general.
I expected it to import helloworld.jsx and hence be able to run the helloWorld function. It threw up the error described above.
//Library importing function from https://gist.github.com/jasonrhodes/5286526
// indexOf polyfill from https://gist.github.com/atk/1034425
[].indexOf||(Array.prototype.indexOf=function(a,b,c){for(c=this.length,b=(c+~~b)%c;b<c&&(!(b in this)||this[b]!==a);b++);return b^c?b:-1;});
var Libraries = (function(libPath) {
return {
include: function(path) {
if (!path.match(/\.jsx$/i)) {
path = path + ".jsx";
}
return $.evalFile(libPath + path);
}
};
})($.fileName.split("/").splice(0,$.fileName.split("/").indexOf("adobe_scripts") + 1).join("/") + "/lib/");
Libraries.include("HelloWorld.jsx");
helloWorld();
It's many moons since I did this stuff ...
Isn't Libraries a function that takes a libPath, so you would need to call
Libraries('c:\whereever').include('HellowWorld.jsx');
Firstly: as far as I can tell, this is not a duplicate. The other questions with similar problems are all slightly different, e.g. use a transformation like babel or have problems with transitive imports. In my case I have no transformation, I have one test file and one file imported file that will be tested. I just started using jest and use the default setting, so there is no configuration file to post.
When I try to run my tests I get the error message:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
The tested file:
export function showTooltip(x, y, content) {
const infoElement = document.getElementById('info');
infoElement.style.left = `${x}px`;
infoElement.style.top = `${y}px`;
infoElement.style.display = 'block';
infoElement.innerText = createTooltipText(content);
}
function createTooltipText(object) {
return Object.keys(object)
.filter(key => key != 'id')
.map(key => `${key} : ${object[key]}`)
.join('\n');
}
export function hideTooltip() {
const infoElement = document.getElementById('info');
infoElement.style.display = 'none';
}
The test:
import {showTooltip, hideTooltip} from '../../../src/public/javascripts/tooltip.js';
const TOOLTIP_DUMMY = {
style: {
left: 0,
top: 0,
display: '',
innerText: ''
}
};
test('showTooltip accesses the element with the id \'info\'', () => {
const getElementByIdMock = jest.fn(() => TOOLTIP_DUMMY);
document.getElementById = getElementByIdMock;
showTooltip(0, 0, {});
expect(getElementByIdMock).toHaveBeenCalledWith('info');
});
test('hideTooltip accesses the element with the id \'info\'', () => {
const getElementByIdMock = jest.fn(() => TOOLTIP_DUMMY);
document.getElementById = getElementByIdMock;
hideTooltip();
expect(getElementByIdMock).toHaveBeenCalledWith('info');
});
As you can see I am using plain javascript so I am not sure what to do here. The error message gives further hints about Babel which does not really apply to my case.
Sidenote: My test might be flawed. I am currently trying to figure out how to use mocks to avoid interaction with the document and I am not sure if that is the way. However this is not the point of this question as it should not affect the ability of the tests to run, but I am very open for suggestions.
EDIT: Why this is not a duplicate to this question: It kind of is, but I feel that question and the accepted answer were not really helpful for me and hopefully someone will profit from this one.
I have found the solution to my problem:
As suggested in this answer, you need to use Babel. This can be done as suggested here, but I used #babel/env-preset as it is suggested on the Babel website.
This left me with the problem that jest internally uses babel-core#6.26.3, but at least babel 7 was required. This problem is described here. I used the temporary fix of manually copying and overwriting babel-core from my node-modules directory to the node-modules directories of jest-config and jest-runtime. This dirty fix is also described in the previous link.
I have yet to find a clean solution, but at least this works.
Use global.document.getElementById = getElementByIdMock; In some configurations Jest doesn't have access to document object directly.
I'm struggling to come up with a pattern that will satisfy both my tests and ability for Travis to run my script.
I'll start off by saying that the way I have Travis running my script is that I specify the script to be run via node-babel command in my travis.yml as so:
script:
- babel-node ./src/client/deploy/deploy-feature-branch.js
That means when babel-node runs this, I need a method to auto run in deploy-feature-branch.js which I have. That's the line let { failure, success, payload } = deployFeatureBranch(). That forces deployFeatureBranch() to run because it's set to a destructure command.
In there I also have an options object:
let options = {
localBuildFolder: 'build',
domain: 'ourdomain',
branch: process.env.TRAVIS_PULL_REQUEST_BRANCH
}
During a PR build, travis automatically sets the value for process.env.TRAVIS_PULL_REQUEST_BRANCH. That's great! However the way I've set up this module doesn't work so well for tests. The problem I have is that if I try to set options from my test, for some reason the options object isn't being set.
I guess the problem I want to address is first and foremost, why options isn't being set when I try to set them from my test. And then is there a better way to design this module overall.
Test
import {options, deployFeatureBranch } from '../../../client/deploy/deploy-feature-branch'
it.only('creates a S3 test environment for a pull request', async () => {
options.branch = 'feature-100'
options.domain = 'ourdomain'
options.localDeployFolder = 'build'
const result = await deployFeatureBranch()
expect(result.success).to.be.true
})
})
When deployFeatureBranch() runs above in my test, the implementation of
tries to reference options.branch but it ends up being undefined even though I set it to be 'feature-100'. branch is defaulted to process.env.TRAVIS_PULL_REQUEST_BRANCH but I want to be able to override that and set it from tests.
deploy-feature-branch.js
import * as deployApi from './deployApi'
let options = {
localBuildFolder: 'build',
domain: 'ourdomain',
branch: process.env.TRAVIS_PULL_REQUEST_BRANCH
}
const deployFeatureBranch = async (options) => {
console.log(green(`Deploying feature branch: ${options.branch}`))
let { failure, success, payload } = await deployApi.run(options)
return { failure, success, payload }
}
let { failure, success, payload } = deployFeatureBranch(options)
export {
options,
deployFeatureBranch
}
I can't really think of a better way to structure this and also to resolve the setting options issue. I'm also not limited to using Node Modules either, I would be fine with ES6 exports too.
Instead of exporting options and modifying it, just pass in your new options object when calling the function in your test:
import {deployFeatureBranch } from '../../../client/deploy/deploy-feature-branch'
it.only('creates a S3 test environment for a pull request', async () => {
const options = {
branch: 'feature-100',
domain: 'ourdomain',
localDeployFolder: 'build'
};
const result = await deployFeatureBranch(options)
expect(result.success).to.be.true
})
});
The reason it isn't working is because your deployFeatureBranch() function expects options to be passed in when you call it, which you aren't doing.
Also, exporting and changing an object, while it might work, is also really weird and should be avoided. Creating a new object (or cloning the exported object) is definitely the way to go.
I am pretty new to Javascript and I've across an issue I can't seem to understand. The console is complaining saying it is not recognising the function.
I have the following code (GraphActions.js):
var VentureDispatcher = require('../dispatcher/VentureDispatcher');
var GraphStoreConstants = require('../constants/GraphStoreConstants');
var StockService = require('../utils/StockService');
var GraphActions = {};
GraphActions.getGraphData = function(request){
//have the API call here.
VentureDispatcher.handleViewAction({
actionType: GraphStoreConstants.GET_GRAPH_DATA,
data: request
});
StockService.getHistoricStockData(request);
};
GraphActions.recieveGraphData = function(response){
VentureDispatcher.handleServerAction({
actionType: GraphStoreConstants.GRAPH_DATA_RESPONSE,
response: response
});
};
GraphActions.test = function(){
console.debug("found this.");
};
module.exports = GraphActions;
And the following Javascript file which is calling the function within the code above:
var request = require('superagent');
var GraphActions = require('../actions/GraphActions');
var StockService = {
getHistoricStockData: function(symbol){
request.post('http://localhost:8080/historicalData')
.send({
"symbol":"GOOG",
"from":"2016-06-01",
"to":"2016-07-01"
})
.set('Content-Type','application/json')
.end(function(err,res){
if(err || !res.ok){
console.error('Error making request!');
}else {
console.debug(JSON.stringify(res.body));
GraphActions.recieveGraphData(res.body);
}
});
}
};
module.exports = StockService;
The console is throwing the following error and not too sure why:
venture-stock.js:44673 Uncaught TypeError: GraphActions.recieveGraphData is not a function.
Does anyone understand why this is happening? The same method has been called in another place with no problem.
When debugging the code and evaluating GraphAction object in the above code I get the following where the functions defined above are not available:
Where as in another location, the functions are available:
Any help would be appreciated!
It happens because of a circular reference among your modules. The module GraphActions requires the module StockService, which also requires GraphActions. It doesn't matter if what's actually needed in StockService it's the recieveGraphData method, while the method that requires StockService is getGraphData: the module loader doesn't have this level of code analysis. require would always return the whole module, so it's a circular reference.
So, what happens in these cases?
The module loader, when loading GraphActions, meets require('../utils/StockService'), then stops the execution of GraphActions in order to load StockService. At this point, the exported properties of GraphActions are... none. So that's why in StockService you get an empty object.
Solution A
Merge the two modules in one, e.g.
var GraphService = {
getGraphData: function(request) {
...
GraphService.getHistoricStockData(request);
},
recieveGraphData: function(response) { ... },
getHistoricStockData: function(symbol) {
...
GraphService.recieveGraphData(res.body);
}
};
module.exports = GraphService;
Solution B
It's the opposite, i.e. decouple getGraphData and recieveGraphData in two different modules. I don't really like this one, because it can lead to excessive module fragmentation.
Solution C (recommended)
As long as CommonJS is used, you can take advantage of using require wherever you want, so in StockService.js you'd do:
getHistoricStockData: function(symbol) {
request.post('http://localhost:8080/historicalData')
...
.end(function(err, res) {
...
var GraphActions = require('../actions/GraphActions');
GraphActions.recieveGraphData(res.body);
});
}
Now, since the execution will not immediately require GraphActions, the module will be loaded later when all its dependencies are fully loaded, so you'll get a fully working module.
Differences with ES2015 modules
In ES6, you cannot use import outside the root level, i.e. you can't use import inside a function. So you couldn't use solution C with ES6 syntax?
Actually that wouldn't be necessary to begin with, because CommonJS module resolution is different than ES6's, the latter being more advanced. So, you'd have written:
import * as StockService from '../utils/StockService';
...
export function getGraphData(request) {
...
StockService.getHistoricStockData(request);
};
export function recieveGraphData(response) { ... };
export function test() { ... };
and in StockService.js:
import * as GraphActions from '../actions/GraphActions';
...
export function getHistoricStockData(symbol) {
request.post('http://localhost:8080/historicalData')
.send( ... )
.set('Content-Type','application/json')
.end(function(err, res) {
...
GraphActions.recieveGraphData(res.body);
});
};
So what happens here? GraphActions.js is loaded, the import statement is reached, the execution of the module stops and then StockService is loaded. So far, it's the same as CommonJS.
In StockService, the loader would meet the import statement, then the execution of GraphActions resumes and the whole module is correctly exported. So you'd be golden from the start with ES6.
This will lead us to
Solution D (most recommended... if you can)
Since you're already using Babel, use the plugin "transform-es2015-modules-commonjs" or "preset-es2015" and use ES6 modules instead.
Hope this cleared your doubts!
(Btw, it's "receive", not "recieve" ;)
I follow this pattern to organize my js application.
As that example says our application should has the single entry point. File application.js doing that work.
// Filename: application.js
var chat = {
// Create this closure to contain the cached modules
module: function() {
// Internal module cache.
var modules = {};
// Create a new module reference scaffold or load an
// existing module.
return function(name) {
// If this module has already been created, return it.
if (modules[name]) {
return modules[name];
}
// Create a module and save it under this name
return modules[name] = { Views: {} };
};
}()
};
// Using the jQuery ready event is excellent for ensuring all
// code has been downloaded and evaluated and is ready to be
// initialized. Treat this as your single entry point into the
// application.
jQuery(function($) {
$(document).ready(function(){
var foo = new Application.module('Chat').Collection();
});
});
// Filename: chat-module.js
(function(chat){
chat.Model = Backbone.Model.extend({ ... }),
chat.Collection = Backbone.Collection.extend({ ... }),
})(Application.module('Chat'));
It seems well but if try to define chat module for example and invoke it later I have the following error:
Uncaught TypeError: Property 'Collection' of object #<Object> is not a function
I think that error due jQuery ready invokes when chat-module.js not available yet.
How can I resolve that problem?
Your code creates an object and assigns it to the global variable chat, which has a module function as a property:
var chat = {
module: function...
};
...but then when you use it, you use Application.module rather than chat.module.
Application.module('Chat')
and
var foo = new Application.module('Chat').Collection();
It seems to me that your chat variable should be called Application.
Also note that you're using module in two different ways, both with new and without. Without would be the correct use based on your code. It will work both ways because module returns a function (which is an object), and so that will override the normal new behavior, but it means that using new serves no purpose and is misleading to someone reading the code.