How to execute custom asynchronous manipulations inside Gulp `.pipe`? - gulp

gulp-intercept allows synchronous manipulations with file:
import gulpIntercept from "gulp-intercept";
import VinylFile from "vinyl";
// ...
.pipe(gulpIntercept((compiledHtmlFile: VinylFile): VinylFile => {
doSomethingSyncWith(compiledHtmlFile)
return compiledHtmlFile;
}))
// ...
But I am not sure it will not work for asynchronous manipulations because it has not been documented. Well, when this package was development, maybe even Promises did not exist in ECMAScript.
import gulpIntercept from "gulp-intercept";
import VinylFile from "vinyl";
// ...
// I suppose it will not work.
.pipe(gulpIntercept(async (compiledHtmlFile: VinylFile): VinylFile => {
// does "gulp.pipe" really will wait until it ends?
await doSomethingAsyncWith(compiledHtmlFile)
return compiledHtmlFile;
}))
// ...
There is should be another gulp plugin for for asynchronous manipulations instead. Would you please to teach me it?

Looks like the library you are using doesn't support async functions as an input (as you already said!) but you don't need to use the library at all you can use event-stream and map your function and call callback at the end in following manner:
import VinylFile from "vinyl";
const es = require('event-stream');
const gulp = require('gulp');
// ..
.pipe(
es.map(
async (compiledHtmlFile: VinylFile, callback: any): VinylFile => {
// "gulp.pipe" will really wait until it ends!!!
await doSomethingAsyncWith(compiledHtmlFile);
callback(null, compiledHtmlFile);
}
)
)
// ...
or use with then:
// ..
.pipe(
es.map(
async (compiledHtmlFile: VinylFile, callback: any): VinylFile => {
doSomethingAsyncWith(compiledHtmlFile)
.then(() => {
callback(null, compiledHtmlFile);
});
}
)
)
// ...

Related

Taken two pages back since render function is called twice

I want to go back to the previous page when Apollo Client error.graphQLErrors has an error with a specific message from the back-end server,
Below is the snippet of my code.
const Detail = () => { const { snackbar } = useSnackbar();
const history = useHistory();
return(
<Compo query={graphQLQuery}>
{({ data, error, }) => {
if(error?.graphQLErrors[0]?.extensions?.debugMessage.includes('Specific Error')){
history.goBack();
snackbar('Specific Error');
return <></>;
}
else{
//render another component
}
}
}
</Compo>);
Issue is since the render is called twice, when the error happens, history.goBack() is executed twice and I'm taken two pages back.
I'm able to avoid this by removing <React.StrictMode> encapsulating <App> component.
Is there a better way to do this?
I'm trying to avoid removing <React.StrictMode> since it's been there since a long time.
Issue
The issue here is that you are issuing an unintentional side-effect from the render method. In React function components the entire function body is considered to be the "render" method. Move all side-effects into a useEffect hook.
Solution
Since the code is using a children function prop you'll need to abstract what the "child" is rendering into a React component that can use React hooks.
Example:
const DetailChild = ({ data, error }) => {
const history = useHistory();
const { snackbar } = useSnackbar();
const isErrorCondition = error?.graphQLErrors[0]?.extensions?.debugMessage.includes('Specific Error'
useEffect(() => {
if (isErrorCondition)) {
history.goBack();
snackbar('Specific Error');
}
}, [error]);
return isErrorCondition
? null
: (
... render another component ...
);
};
...
const Detail = () => {
return (
<Compo query={graphQLQuery}>
{({ data, error }) => <DetailChild {...{ data, error }} />}
</Compo>
);
};

How to use code splitting on a ressouce (.json, .csv, etc.) with React

Issue:
I am using react-intl and I would like to load the language-related JSON file only when it is needed, in brief to lazy load it.
Documentation :
https://reactjs.org/docs/code-splitting.html
the problem here is I wish to lazy load a JSON file not a component, so I am a bit lost about how to use it without JSX.
Actual code :
import React from 'react';
import {IntlProvider} from 'react-intl';
import English from "../i18n/en.json";
import Polish from "../i18n/pl.json";
const local = navigator.language;
let lang;
switch (local){
case "pl":
case "pl-pl":
lang = Polish;
break;
default:
lang = English;
}
function Wrapper () {
[...]
}
export default Wrapper
What I try which did not works :
test 1:
import React from 'react';
import {IntlProvider} from 'react-intl';
const English = React.lazy(() => import('../i18n/en.json'));
const Polish = React.lazy(() => import('../i18n/pl.json'));
const local = navigator.language;
let lang;
switch (local){
case "pl":
case "pl-pl":
lang = Polish;
break;
default:
lang = English;
}
function Wrapper () {
[...]
}
export default Wrapper
test 2:
import React from 'react';
import {IntlProvider} from 'react-intl';
const English = React.lazy(() => import('../i18n/en.json'));
const Polish = React.lazy(() => import('../i18n/pl.json'));
const local = navigator.language;
let lang;
switch (local){
case "pl":
case "pl-pl":
Polish.then(polish => {lang = polish});
break;
default:
English.then(english=> {lang = english});
}
function Wrapper () {
[...]
}
export default Wrapper
test 3 (inspired from How to import Json file with React lazy loading?) :
import React from 'react';
import {IntlProvider} from 'react-intl';
const local = navigator.language;
let lang;
switch (local){
case "pl":
case "pl-pl":
import("../i18n/pl.json").then(Polish=> {
lang = Polish);
});
break;
default:
import("../i18n/en.json").then(English=> {
lang = English);
});
}
function Wrapper () {
[...]
}
export default Wrapper
In case more code is needed (the function Wrapper for example), please let me know in a comment :)
I also had the same issue where I wanted to code split my JSON in React. I found a work around that uses the dynamic import function.
I wasn't using Intl so I can't confirm this works for your needs but this is what worked for my needs (and I think it should work for you)
I created a function to return a promise with my data that I needed to code split. I then called this in my Component in an Async func with an await on the data.
My use case was to fetch data from an API, if that was done, load a cached JSON of the data.
const loadCached = (key) => {
return new Promise((res, rej) => {
import(`../datasets/cached/${key}.json`).then((data) => {
res(data?.default);
});
});
};
I then called it from my async catch
const cachedData = await loadCached(key);
So for you use I would keep my loadCached function (maybe rename it to like loadLang)
then wrap it in a useEffect to fire on load to change the language and gather the JSON.
Here is how I would approach your issue (untested code)
const local = navigator.language; // get language
const [lang, setLang] = useState("English"); // for a default of english
// fire when we get local from DOM
useEffect(() => {
async function getLangData() {
const response = await loadLang(local);
setLang(reponse);
}
getLangData();
}, [local])
const setLang = (lang) => {
return new Promise((res, rej) => {
import(`../i18n/${lang}.json`).then((data) => {
res(data?.default);
});
});
};

React Native run function any outher screen

I have a simple question.
I want to run the function I created in App.js in a different screen.
Each screen is connected to an AppContainer in context.
I'm going to use state on another screen, but I do not know how to do it.
Can you give me a simple example?
_setDefaultLocation = locationKey => {
console.log("call _setDefaultLocation function");
this.setState({ defaultLocation: locationKey });
console.log(this.state.defaultLocation);
this._getCurrent(locationKey);
};
The above function is called this.props._setDefaultLocation (); I tried to do this and it did not work.
import React from "react";
const WeatherContext = React.createContext();
function withWeather(WrappedComponent) {
return function withContext(props) {
return (
<WeatherContext.Consumer>
{value => <WrappedComponent value={value} {...props} />}
</WeatherContext.Consumer>
);
};
}
export { WeatherContext, withWeather };
Context is used to make it like that.
You can use global keyword to make your function global. Then you can use it in any screen.
For example, assume that you have a function is called 'myFunction'. You can implement your function in App.js as following.
global.myFunction = () => {
console.warn('I am a global function...')
// your function body
};
Then you can call this function in any screen just like other function calling..
In your case your function can implement in this format.
global._myFunction = locationKey => {
console.warn('I am a global function...')
// your function body
};
And when you are calling the function call it as _myFunction(locationKey).
NOTE: 'locationKey' is the parameter that you should pass for your function.
Think this solution will help you.

Spying on an imported function that calls another function in Jest

I'm trying to spy on a function that's called by another function, both of which reside in an external file and imported.
Funcs.spec.js:
import * as Funcs from './Funcs'
describe('funcA', () => {
it('calls funcB', () => {
jest.spyOn(Funcs, 'funcB')
Funcs.funcA()
expect(Funcs.funcB).toHaveBeenCalled()
}
}
Funcs.js:
export const funcA = () => {
funcB()
}
export const funcB = () => {}
For some reason the spy is not respected in scope of Funcs.js. What can I do to spy on funcB so I know funcA has called it?
Only methods can be spied. There is no way to spy on funcB if it's called directly like funcB() within same module.
In order for exported function to be spied or mocked, funcA and funcB should reside in different modules.
This allows to spy on funcB in transpiled ES module (module object is read-only in native ESM):
import { funcB } from './b';
export const funcA = () => {
funcB()
}
Due to that module imports are representations of modules, this is transpiled to:
var _b = require('./b');
var funcA = exports.funcA = function funcA() {
(0, _b.funcB)();
};
Where funcB method is tied to _b module object, so it's possible to spy on it.
The problem you describe is referenced on a jest issue.
A possible solution to your problem (if you want to keep the functions inside the same file) is to use CommonJS, consider the following example:
fns.js
exports.funcA = () => {
exports.funcB();
};
exports.funcB = () => {};
fns.spec.js
const fns = require("./fns");
describe("funcA", () => {
it("calls funcB", () => {
fns.funcB = jest.fn();
fns.funcA();
expect(fns.funcB).toBeCalled();
});
});

How do I load a WASM module in a Vue component without initializing the module every time?

I have created a Rust library of type cdylib using
cargo web build --target=wasm32-unknown-unknown
I use a modified version of the "rust-wasm-loader" NPM package to build and load the WASM file. rust-wasm-loader uses this as a way to use the Rust code:
const wasm = require('./main.rs')
wasm.initialize().then(module => {
// Use your module here
const doub = module.cwrap('doub', 'number', ['number'])
console.log(doub(21))
})
I do not want to initialize the module every time I want to use the code. How do I load the module and use it like a library?
Since the loading of WebAssembly is asynchronous and may actually take some time for large modules, you need to handle the state when the module is not loaded, and then let the rest of the application know when the WebAssembly module is loaded.
You do not say how you are handling state in your Vue application, but if you are e.g. using Vuex you can do something like this:
const doubPlugin = store => {
wasm.initialize().then(module => {
const doub = module.cwrap('doub', 'number', ['number'])
store.subscribe((mutation, state) => {
if (mutation.type === 'DOUB_REQUEST') {
store.commit('DOUB_RESULT', doub(mutation.payload))
}
})
store.commit('DOUB_READY')
})
}
const store = new Vuex.Store({
state,
mutations,
plugins: [doubPlugin]
})
I've done a similar thing in an Elm/WebAssembly application (relevant JavaScript), so if you want to see how this can be applied in practice you can check that out.
Making a wrapper JS module that performs initialization and re-exports the promise seems like the most straightforward approach.
// main.js
module.exports = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
Then anything can do
require("./main.js").then(api => {
console.log(api.doub(21));
});
and will always get the same module. Or alternatively you could invert the async part and do
// main.js
const api = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
exports.doub = async function (val) {
return (await api).doub(val);
};
Then users of your module could do
const api = require("./main.js");
api.doub(21).then(result => {
console.log(result);
});
I created a class to wrap the WebAssembly loading and created a cwrap for every function:
class mkLib {
ready = false
_mod = require("./main.rs").initialize().then(module => {
this._mod = module
this.doub = module.cwrap('doub', 'number', ['number'])
this.ready = true
})
}
export default mkLib
In the Vue component's data there is a variable for the new class and in watch I wait for a change in the ready property:
data () {
return {
mod: new mkLib,
ready: false
}
},
watch: {
'mod.ready': function () {
this.ready = true
// now this.mod.FUNC(PARAMS) can be used
console.log(this.mod.doub(20))
}
}