es6-module default export importing as undefined - javascript

I'm not sure what I'm missing here. I'm working on a project using jspm and es6-module-loader. I've got an module defined as follows:
import hooks from './hooks';
import api from './api';
import tools from './tools';
const StencilUtils = {
hooks: hooks,
api: api,
tools: tools,
};
export {hooks, api, tools};
export default StencilUtils;
/* global define */
(function(root) {
if (typeof define === 'function' && define.amd) {
define(function() {
return (root.stencilUtils = StencilUtils);
});
} else if (typeof module === 'object' && module.exports) {
module.exports = StencilUtils;
} else {
window.stencilUtils = StencilUtils;
}
}(this));
I'm importing this module in another file, and using it like so:
import utils from 'bigcommerce/stencil-utils';
utils.hooks.on('cart-item-add', (event, form) => {
// do stuff
});
When the files load, I get an error that utils is undefined. If I change the file to this:
import {hooks} from 'bigcommerce/stencil-utils';
hooks.on('cart-item-add', (event, form) => {
// do stuff
});
It works correctly. So, it appears something is not working correctly with the default export statement. Is there something obviously wrong here with the import or export statements that would cause this issue?

I think there are two things around this issue:
When you have named exports your access them through either importing as library or with object destruction.
Method 1
xyz.js
export const a = 1;
abc.js
import {a} from "xyz";
Method 2
xyz.js
export const a = 1;
abc.js
import {* as myModule} from "xyz";
console.log(myModule.a);
So in your case
export {hooks, api, tools};
it can be either
import * as utils from 'bigcommerce/stencil-utils';
or
import {hooks} from 'bigcommerce/stencil-utils';
Default export statement is not proper
It can either be
export default {
hooks: hooks,
api: api,
tools: tools,
};
Or
const StencilUtils = {
hooks: hooks,
api: api,
tools: tools,
};
export { StencilUtils as default };
Hope this will help you. For more details see this

Related

How to access Vue 3 global properties from the store

In Vue 2 I used to import Vue and access global properties like this (from the store):
import Vue from 'vue'
Vue.config.myGlobalProperty
According to the new documentation, in Vue 3 the global properties are declared using the app object returned by createApp:
const app = createApp({})
app.config.globalProperties.myGlobalProperty
And then accessed in the child component by simply calling this.myglobalProperty
But how to access that global property from the store? I tried exporting/importing the app object but it doesn't work (probably due to the app being created after its import in the store).
With Vue 2 I used to use global properties in the store like this:
Declaration in the main.js file:
import Vue from 'vue'
Vue.config.myglobalProperty = 'value'
Usage in the store:
import Vue from 'vue'
Vue.config.myglobalProperty
Is there a good way to do that in Vue3?
I noticed a better way to provide/inject properties but it works with child component only and not with the store.
You could pass the app instance to a store factory:
// store.js
import { createStore as createVuexStore } from 'vuex'
export const createStore = (app) => {
return createVuexStore({
actions: {
doSomething() {
if (app.config.globalProperties.myGlobal) {
//...
}
}
}
})
}
And use it in main.js like this:
// main.js
import { createApp } from 'vue'
import { createStore } from './store'
const app = createApp({})
const store = createStore(app)
app.use(store)
app.mount('#app')
If your store modules need access to the app instance, you could use the same technique above with a module factory:
// moduleA.js
export default (app) => {
return {
namespaced: true,
actions: {
doSomething() {
if (app.config.globalProperties.myOtherGlobal) {
//...
}
}
}
}
}
And import it into your store like this:
// store.js
import moduleA from './moduleA'
import { createStore as createVuexStore } from 'vuex'
export const createStore = (app) => {
return createVuexStore({
modules: {
moduleA: moduleA(app),
}
})
}
If you do not use Vuex, etc., you can easily create your store via provide/inject on the application itself, as in the example (the example is simplified for understanding):
const createState = () => reactive({counter: 0, anyVariable: 1});
const state = createState();
const key = 'myState';
// example of reactivity outside a component
setInterval(() => {
state.counter++;
}, 1000);
const app = createApp({});
app.provide('myState', state); // provide is used on the whole application
As you can see, your own store can be completely used outside the component.
Inside components, you can use (example):
setup() {
...
const globalState = inject('myStore'); // reactive => {counter: 0, anyVariable: 1}
...
return {globalState, ...}
}
Accordingly, you can have multiple stores in multiple Vue applications.
I hope this example will help you somehow.

i18n support is not compatible with next export. (SSR - NextJS 10)

i18n support is not compatible with next export.
NextJS dont run the deploy with i18n
Im using nextJS 10, and the main reason that i choose next 10, is that i can do SSR and use the i18n.
Internationalized Routing its a new next js 10 feature and have a page only to tha feature.
But when im gonna do a deploy, this error appears: i18n support is not compatible with next export.
Theres nothing about this in internationalized routing page.
My code
next.config.js
const withImages = require('next-images')
const path = require('path')
module.exports = withImages({
esModule: false,
i18n: {
locales: ['en-US', 'pt-BR', 'pt-PT', 'es-ES'],
defaultLocale: 'pt-BR',
},
});
I created a translate archive that make the condition with next router
obs: PT and EN are JSON files with text
import * as pt from "./pt";
import * as en from './en';
import { useRouter } from "next/router"
export const traducao = () =>{
let routes = useRouter();
let translate;
if (routes.locale == 'pt-PT' || routes.locale == 'pt-BR') {
translate = pt.default;
} else {
translate = en.default;
}
return translate
}
And the i just use in my project like a function:
{traducao().homeText.button_text}
Work well, recognizes the browser language and switch.
Im using deploy script
npm run deploy
"deploy": "npm run clean && npm run build && next export -o dist/"
Steps to reproduce
Go to 'next.config,js'
create the i18n export
create a Translate file that recognizes the browser language
import JSONs files with your site text
Use where you want
Try to deploy
Expected behavior
Its just suppose to work fine and Deploy normal.
Screenshots
System information
OS: Linux Ubuntu
IDE: VSCode
Version of Next.js: 10
Version of Node.js: v15.3.0
Deployment: next deploy
Digging through issues on vercel's github I found this alternative that doesn't use next-i18next or any other nextjs magic:
https://github.com/Xairoo/nextjs-i18n-static-page-starter
It's just basic i18n using i18next that bundles all locale together with JS, so there are obvious tradeoffs but at least it works with SSG. You can build upon that to come up with something more elaborate. That's what I will do.
You can't use export with next.js i18n implementation.
Note that Internationalized Routing does not integrate with next export as next export does not leverage the Next.js routing layer. Hybrid Next.js applications that do not use next export are fully supported.
Next.js docs
There's an alternative, by not using the i18n feature of next.js completely and creating the i18n language detection yourself.
An example that uses the next-language-detector module is described in this blog post and may look like this:
// languageDetector.js
import languageDetector from 'next-language-detector'
import i18nextConfig from '../next-i18next.config'
export default languageDetector({
supportedLngs: i18nextConfig.i18n.locales,
fallbackLng: i18nextConfig.i18n.defaultLocale
})
// redirect.js
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import languageDetector from './languageDetector'
export const useRedirect = (to) => {
const router = useRouter()
to = to || router.asPath
// language detection
useEffect(() => {
const detectedLng = languageDetector.detect()
if (to.startsWith('/' + detectedLng) && router.route === '/404') { // prevent endless loop
router.replace('/' + detectedLng + router.route)
return
}
languageDetector.cache(detectedLng)
router.replace('/' + detectedLng + to)
})
return <></>
};
export const Redirect = () => {
useRedirect()
return <></>
}
// eslint-disable-next-line react/display-name
export const getRedirect = (to) => () => {
useRedirect(to)
return <></>
}
The complete guide and the example code can be found here:
guide
example
Hello I show you my soluce with only i18n-js
// i18n.ts
import i18n from "i18n-js";
import en from "./en.json";
import fr from "./fr.json";
const localeEnable = ["fr", "en"];
const formatLocale = () => {
const { language } = window.navigator;
if (language.includes("en")) return "en";
if (language.includes("fr")) return "fr";
if (!localeEnable.includes(language)) return "en";
return "en";
};
// Set the key-value pairs for the different languages you want to support.
i18n.translations = {
en,
fr,
};
// Set the locale once at the beginning of your app.
i18n.locale = "en";
const useTranslate = () => {
return (t: string) => {
if (typeof window !== "undefined") {
i18n.locale = formatLocale();
}
return i18n.t(t);
};
};
export default useTranslate;
// home.tsx
import useTranslate from "../locales/i18n";
const t = useTranslate();
return (<p>{t("idstring")}</p>)

How to access plugin from nuxt.js store?

I have this gtag (analytics plugin) that I can access on my components but never on my store.
I would appreciate any opinions. Thanks
plugins/vue-gtag.js
import Vue from "vue"
import VueGtag from "vue-gtag"
export default ({ app }, inject) => {
Vue.use(VueGtag, {
config: {
id: process.env.ga_stream_id
}
})
}
store/gaUserProperty.js
import Vue from "vue"
import { User } from "~/models/user/User"
export const states = () => ({})
const getterObjects = {}
const mutationObjects = {}
Object.keys(states).forEach(key => {
getterObjects[key] = state => state[key]
mutationObjects[key] = (state, value) => (state[key] = value)
})
export const state = () => states
export const getters = { ...getterObjects }
export const mutations = { ...mutationObjects }
export const actions = {
async sendUserProperties({ dispatch, commit }) {
let res = await this.$UserApi.getUser()
if (!(res instanceof User)) {
} else {
// I can access this on my components and pages but for some reason not here....
console.log(this.$gtag)
}
}
}
To import this properly, I would export the instance (or any of its internals) from main.(ts|js):
const Instance = new Vue({...whatever});
// only export what you need in other parts of the app
export const { $gtag, $store, $t, $http } = Instance;
// or export the entire Instance
export default Instance;
now you can import it in your store:
import Instance from '#/main';
// or:
import { $gtag } from '#/main';
// use Instance.$gtag or $gtag, depending on what you imported.
As other answers mentioned, in current Vuex version the Vue instance is available under this._vm inside the store. I'd refrain from relying on it, though, as it's not part of the exposed Vuex API and not documented anywhere. In other words, Vuex developers do not guarantee it will still be there in future versions.
To be even more specific, Vue's promise is that all v2 code will work in v3. But only if you use the exposed API's.
And the discussion here is not even on whether it will be removed or not (it most likely won't). To me, it's more a matter of principle: if it starts with a _ and it's not documented, it translates into a message from authors: "We're reserving the right to change this at any time, without warning!"
You can access the Vue instance through this._vm in the Vuex store, so you would just need to do:
console.log(this._vm.$gtag)
That should do the trick.
According to nuxtjs the plugins are available in the store actions.
https://nuxtjs.org/docs/directory-structure/plugins
Sometimes you want to make functions or values available across your
app. You can inject those variables into Vue instances (client side),
the context (server side) and even in the Vuex store. It is a
convention to prefix those functions with a $.

Can not import functions corretly in reactJS App?

I am having problem with import, I have a file like this :
import { TYPE_CONTRAT_UPDATE, CONFORMITE_UPDATE } from "./actionsTypes";
import { createAction } from "../../../../../../redux/Utilities";
const updateTypeContrat = (idContrat, data, success, error) =>
createAction(TYPE_CONTRAT_UPDATE.PUT_CALL, { idContrat, data, success,
error });
const updateConformiteContrat = (idContrat, data, success, error) =>
createAction(CONFORMITE_UPDATE.PUT_CALL, { idContrat, data, success,
error });
export default { updateTypeContrat, updateConformiteContrat};
I am trying to import as you can see variables in capital from my file actionsTypes, here is the file :
import { createPutTypes } from "../../../../../../redux/Utilities";
const TYPE_CONTRAT_UPDATE = createPutTypes("TYPE_CONTRAT_UPDATE");
const CONFORMITE_UPDATE = createPutTypes("CONFORMITE_UPDATE");
export default { CONFORMITE_UPDATE, TYPE_CONTRAT_UPDATE }
But I get an error :
Line 1: TYPE_CONTRAT_UPDATE not found in './actionsTypes' import/named
Line 1: CONFORMITE_UPDATE not found in './actionsTypes' import/named
Any help would be much appreciated.
You need to use named export instead of the default.
import { createPutTypes } from "../../../../../../redux/Utilities";
export const TYPE_CONTRAT_UPDATE = createPutTypes("TYPE_CONTRAT_UPDATE");
export const CONFORMITE_UPDATE = createPutTypes("CONFORMITE_UPDATE");
Those imports, as said from the error, should be named exports.
DEFAULT export
A default export (export default [...] is what will be imported when using import X from 'fileX'. There can be only one. No matter what you assign the import to in this case (here, you assign it to X), it will work
// fileX.js
export default Example;
You can do either
import X from 'fileX'; // works, X contains Example
import Example from 'fileX'; // same
...
NAMED exports
A named export (export const TYPE_CONTRACT = [...]) can be used as much as you want, however, the name of the import matters:
// fileX.js
export const Example = [...]
means that the import should be :
import { Example } from 'fileX'; // works properly, Example contains the export of fileX
import { X } from 'fileX'; // won't work, no way to know which export you're referring to
import Example from 'fileX'; // won't work either, this is not a default export.

Stubbing single exported function with Sinon

I have just changed my lodash import from import _ from 'lodash'; to import debounce from 'lodash/debounce';
In my test I used to have sandbox.stub(_, 'debounce').returnsArg(0);, but now I'm stuck as to what to change it to. Obviously sandbox.stub(debounce).returnsArg(0); won't work. Not sure what to do when only a single function is exported from a module.
This syntax:
import something from 'myModule';
...is ES6 syntax which binds something to the default export of 'myModule'.
If the module is an ES6 module then you can stub the default export of the module like this:
import * as myModule from 'myModule';
const sinon = require('sinon');
// ...
const stub = sinon.stub(myModule, 'default');
...but this only works if 'myModule' is an ES6 module.
In this case 'lodash/debounce' is not an ES6 module, it is shipped pre-compiled. The last line is this:
module.exports = debounce;
...which means the module export is the debounce function.
This means that in order to stub 'lodash/debounce' you have to mock the entire module.
Sinon doesn't provide module-level mocking so you will need to use something like proxyquire:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const debounceStub = sinon.stub().returnsArg(0);
const code = proxyquire('[path to your code]', { 'lodash/debounce': debounceStub })
...or if you are using Jest you can use something like jest.mock:
jest.mock('lodash/debounce', () =>
jest.fn((func, ms) => func) // <= mock debounce to simply return the function
);
Details
The reason that stubbing the default export of a module only works if it is an ES6 module is because of what happens during compilation.
ES6 syntax gets compiled into pre-ES6 JavaScript. For example, Babel turns this:
import something from 'myModule';
...into this:
var _myModule = _interopRequireDefault(require("myModule"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ?
obj : // <= return the result of require("myModule") if it is an ES6 module...
{ default: obj }; // <= otherwise set it to the default property of a wrapper object
}
...so if 'myModule' is an ES6 module it gets returned directly...but if it is not then the interop returns a wrapping object.
Since each import gets a different wrapping object, changing the default property on one doesn't affect the default property of any others.
you can make yourself a wrapping file, which will eventually export the same lodash/debounce instance for you, but this time you could stub that, e.g.:
myutils/lodash/debounce.js
import lodashDebounce from 'lodash/debounce';
const exports = {
debounce: lodashDebounce,
};
export const debounce = () => exports.debounce();
export default exports;
now, in your actual code import the debounce not from the original location, but from this wrapping file instead:
BEFORE:
import debounce from 'lodash/debounce' // this is how we usually do
AFTER:
import { debounce } from 'myutils/lodash/debounce' // if we want to stub it
// all other code-lines remain the same
const method = () => {
debounce(callback, 150));
...
}
now when doing a test.js:
import lodashWrapped from 'myutils/lodash/debounce';
sinon.stub(lodashWrapped , 'debounce').callsFake((callbackFn) => {
// this is stubbed now
});
// go on, make your tests now

Categories