167 lines
6.0 KiB
JavaScript
167 lines
6.0 KiB
JavaScript
const { createMacro, MacroError } = require("babel-plugin-macros")
|
|
const { addNamed } = require("@babel/helper-module-imports")
|
|
const path = require("path")
|
|
|
|
// `createMacro` is simply a function that ensures your macro is only
|
|
// called in the context of a babel transpilation and will throw an
|
|
// error with a helpful message if someone does not have babel-plugin-macros
|
|
// configured correctly
|
|
module.exports = createMacro(useMethod)
|
|
|
|
function useMethod({ references, state, babel }) {
|
|
// state is the second argument you're passed to a visitor in a
|
|
// normal babel plugin. `babel` is the `babel-plugin-macros` module.
|
|
// do whatever you like to the AST paths you find in `references`
|
|
// read more below...
|
|
const { default: defaultImport = [] } = references
|
|
|
|
defaultImport.forEach(referencePath => {
|
|
if (referencePath.parentPath.type != "CallExpression") {
|
|
throw new MacroError(
|
|
"useQuery should be called with a function",
|
|
)
|
|
}
|
|
|
|
const ourArguments = referencePath.parentPath.get("arguments")
|
|
|
|
if (ourArguments.length < 1) {
|
|
throw new MacroError(
|
|
"There are not enough arguments",
|
|
)
|
|
}
|
|
else if (ourArguments.length > 3) {
|
|
throw new MacroError(
|
|
"There are too many arguments",
|
|
)
|
|
}
|
|
|
|
if (ourArguments[0].has("callee")) {
|
|
throw new MacroError(
|
|
"function used in useMethod can not be a function call",
|
|
)
|
|
}
|
|
else if (ourArguments.length > 1 && !ourArguments[1].isFunction()) {
|
|
throw new MacroError(
|
|
"useMethods 2nd argument must be a callable function",
|
|
)
|
|
}
|
|
else if (ourArguments.length > 2 && !ourArguments[2].isFunction()) {
|
|
throw new MacroError(
|
|
"useMethods 3rd argument must be a callable function",
|
|
)
|
|
}
|
|
|
|
let declPath = referencePath.parentPath.parentPath;
|
|
|
|
let call = ourArguments[0].node
|
|
let nop = babel.types.arrowFunctionExpression([babel.types.identifier("e")], babel.types.blockStatement([]))
|
|
let onDone = ourArguments[1] ? ourArguments[1].node : nop;
|
|
let onError = ourArguments[2] ? ourArguments[2].node : null;
|
|
|
|
let parsWithSet = referencePath.parentPath.parentPath.get("id").get("elements")
|
|
|
|
if (!parsWithSet[0].isIdentifier())
|
|
throw new MacroError(
|
|
"first result of useMethod must be a function name",
|
|
)
|
|
let runMethod = parsWithSet[0].node
|
|
if (!parsWithSet[1].isObjectPattern())
|
|
throw new MacroError(
|
|
"second result of useMethod must be the returned data object",
|
|
)
|
|
let pars = parsWithSet[1].node
|
|
|
|
let setStateIdentifier = declPath.scope.generateUidIdentifier("setStateInternalMagic")
|
|
|
|
let variableArgsId = declPath.scope.generateUidIdentifier("variableArgsInternalMagic")
|
|
|
|
let useStateHookImport = addNamed(declPath, "useState", "react");
|
|
let useCallbackHookImport = addNamed(declPath, "useCallback", "react");
|
|
|
|
let servicePath = "@services/api";
|
|
let apiErrorImport = addNamed(declPath, "ApiError", servicePath);
|
|
|
|
let nejServicesPath = "@utils/NejManager/NejProvider";
|
|
let emptyErrorIdentifier = declPath.scope.generateUidIdentifier("emptyErrorInternalMagic")
|
|
let onErrorIdentifier = declPath.scope.generateUidIdentifier("onErrorInternalMagic")
|
|
let onSuccessIdentifier = declPath.scope.generateUidIdentifier("onSuccessInternalMagic")
|
|
let onStartLoadingIdentifier = declPath.scope.generateUidIdentifier("onErrorInternalMagic")
|
|
let onStopLoadingIdentifier = declPath.scope.generateUidIdentifier("onErrorInternalMagic")
|
|
|
|
let useOnErrorImport = addNamed(declPath, "useOnError", nejServicesPath);
|
|
let useOnSuccessImport = addNamed(declPath, "useOnSuccess", nejServicesPath);
|
|
let useStartLoadingImport = addNamed(declPath, "useStartLoading", nejServicesPath);
|
|
let useStopLoadingImport = addNamed(declPath, "useStopLoading", nejServicesPath);
|
|
|
|
let useStateSrc = babel.template("[DATA, CALL_SET] = USE_STATE({data: null, error: null, loading: false})")({
|
|
DATA: pars,
|
|
CALL_SET: setStateIdentifier,
|
|
USE_STATE: useStateHookImport
|
|
})
|
|
|
|
declPath.replaceWith(useStateSrc)
|
|
|
|
let src = babel.template(`
|
|
const ON_ERROR_IDENTIFIER = USE_ERROR_IMPORT();
|
|
const ON_SUCCESS_IDENTIFIER = USE_SUCCESS_IMPORT();
|
|
const ON_START_LOADING_IDENTIFIER = USE_START_LOADING_IMPORT();
|
|
const ON_STOP_LOADING_IDENTIFIER = USE_STOP_LOADING_IMPORT();
|
|
|
|
function EMPTY_ERROR(x){
|
|
return null
|
|
}
|
|
|
|
const RUN_NAME = USE_CALLBACK(async (...ARGS) => {
|
|
let notifKey = "";
|
|
try{
|
|
notifKey = ON_START_LOADING_IDENTIFIER();
|
|
CALL_SET({ data: null, error: null, loading: true });
|
|
let ret = await IMPORT_NAME(...ARGS);
|
|
CALL_SET({ data: ret, error: null, loading: false });
|
|
ON_STOP_LOADING_IDENTIFIER(notifKey);
|
|
ON_SUCCESS_IDENTIFIER();
|
|
DONE(ret, ...ARGS);
|
|
return ret;
|
|
}
|
|
catch(e){
|
|
if(e instanceof ERROR_CLASS){
|
|
CALL_SET({data: null, error: e.body, loading: false})
|
|
ON_STOP_LOADING_IDENTIFIER(notifKey);
|
|
ON_ERROR_IDENTIFIER(e.body);
|
|
ERROR(e.body)
|
|
return
|
|
}
|
|
CALL_SET({data: null, error: e, loading: false})
|
|
ON_STOP_LOADING_IDENTIFIER(notifKey);
|
|
ON_ERROR_IDENTIFIER(e);
|
|
ERROR(e)
|
|
return null;
|
|
}
|
|
}, [])
|
|
`)({
|
|
IMPORT_NAME: call,
|
|
CALL_SET: setStateIdentifier,
|
|
RUN_NAME: runMethod,
|
|
ARGS: variableArgsId,
|
|
USE_CALLBACK: useCallbackHookImport,
|
|
ERROR_CLASS: apiErrorImport,
|
|
DONE: onDone,
|
|
//if its null create an empty function to call
|
|
ERROR: onError != null ? onError : emptyErrorIdentifier,
|
|
EMPTY_ERROR: emptyErrorIdentifier,
|
|
|
|
USE_ERROR_IMPORT: useOnErrorImport,
|
|
USE_SUCCESS_IMPORT: useOnSuccessImport,
|
|
USE_START_LOADING_IMPORT: useStartLoadingImport,
|
|
USE_STOP_LOADING_IMPORT: useStopLoadingImport,
|
|
|
|
ON_SUCCESS_IDENTIFIER: onSuccessIdentifier,
|
|
ON_START_LOADING_IDENTIFIER: onStartLoadingIdentifier,
|
|
ON_STOP_LOADING_IDENTIFIER: onStopLoadingIdentifier,
|
|
ON_ERROR_IDENTIFIER: onErrorIdentifier,
|
|
|
|
})
|
|
|
|
declPath.parentPath.insertAfter(src)
|
|
})
|
|
} |