From 814d36ee6c0ca49cb60e56262c142a06dc1c5d0c Mon Sep 17 00:00:00 2001 From: RiN Date: Mon, 3 Oct 2022 02:21:51 +0800 Subject: [PATCH] feat(fetchText): add fetchText API for fetch text response (#23) * test: create test case for fetchText * feat(fetchText): add fetchText API for produce fetch for text response --- README.md | 43 ++++++++++++++- src/fetch.macro.js | 128 ++++++++++++++++++++++++++++++--------------- tests/fetch.js | 12 +++++ 3 files changed, 138 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index adbc3ceb..aa2ca286 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@
-

fetch.macro

+

fetch.macro

Allows you to build fetcher function by URL at compile-time.

+ +

+ Usage • + API • + Contributors +

+
--- @@ -20,8 +27,12 @@ ## Usage +[[Back to the Table of Contents] ↑](#toc) + Simply install and configure [`babel-plugin-macros`](https://github.com/kentcdodds/babel-plugin-macros) and then use `fetch.macro`. +> Some project that build with `create-react-app` doesn't need extra setup for `babel-plugin-macros`. + ### Vite To be able to use these macros in your [Vite](https://vitejs.dev/) project, you only need install [`vite-plugin-babel-macros`](https://github.com/itsMapleLeaf/vite-plugin-babel-macros) and add some configuration in `vite.config.js`. And it just work. @@ -47,11 +58,13 @@ export default { #### Basic Run one of the following command inside your project directory to install the package: + ``` $ npm i fetch.macro or -$ yarn add fetch.macro +$ yarn add fetch.macro ``` + Given the following `Input`: ```javascript @@ -90,8 +103,34 @@ const fetchProject = ({ id, projectId, others, ...opts }) => fetch(`/api/v1/user/${id}/project/${projectId}/${others}`, opts); ``` +## API + +[[Back to the Table of Contents] ↑](#toc) + +### Default + +It will be produce a code for fetch function with URL by input and return response that need to be manual handle the response. + +``` +import f from 'fetch.macro' + +const fetchByUrl = f("/api/v1/ping"); +``` + +### fetchText + +It will be produce a code for fetch function with URL by input and return [**response text**](https://webidl.spec.whatwg.org/#idl-USVString). + +``` +import { fetchText } from 'fetch.macro' + +const fetchLicense = fetchText("https://raw.githubusercontent.com/r17x/fetch.macro/main/LICENSE"); +``` + ## Contributors +[[Back to the Table of Contents] ↑](#toc) + diff --git a/src/fetch.macro.js b/src/fetch.macro.js index 5507ccf4..c15167aa 100644 --- a/src/fetch.macro.js +++ b/src/fetch.macro.js @@ -9,47 +9,89 @@ const getValue = (path) => const isValueHaveArgs = (val) => /:\w+/g.test(val); -module.exports = createMacro(({ babel: { types: t, template }, references: { default: paths } }) => { - paths.forEach(({ parentPath }) => { - const value = getValue(parentPath); +const memberExpressionTemplate = (ref) => + ({ + [true]: "", + [ref === "default"]: "", + [ref === "fetchText"]: ".then(r => r.text())", + /** + * @todo {https://github.com/r17x/fetch.macro/issues/22} + * - [ ] fetchJson + * - [ ] fetchBlob + * - [ ] fetchArrayBuffer + * - [ ] fetchFormData + */ + }.true); + +module.exports = createMacro( + ({ + babel: { types: t, template }, + references: { + default: paths, + fetchText, + /** + * @todo {https://github.com/r17x/fetch.macro/issues/22} + * - [ ] fetchJson + * - [ ] fetchBlob + * - [ ] fetchArrayBuffer + * - [ ] fetchFormData + */ + }, + }) => { + const transform = + (reference) => + ({ parentPath }) => { + const value = getValue(parentPath); + const memberExpression = memberExpressionTemplate(reference); + + if (value) { + if (isValueHaveArgs(value)) { + const buildFetch = template(`(PARAM) => fetch(URI, opts)`.concat(memberExpression)); + parentPath.replaceWithMultiple( + buildFetch({ + PARAM: t.objectPattern( + value + .split("/") + .filter((v) => v.startsWith(":")) + .map((p) => { + const id = t.identifier(p.replace(":", "")); + return t.objectProperty(id, id, false, true); + }) + .concat([t.restElement(t.identifier("opts"))]), + ), + URI: t.templateLiteral( + value + .replace(/:\w+/g, "::::") + .split("::::") + .map((v, i, a) => t.templateElement({ raw: v, cooked: v }, i + 1 === a.length)), + value + .split("/") + .filter((v) => v.startsWith(":")) + .map((v) => t.identifier(v.replace(":", ""))), + ), + }), + ); + } else { + const buildFetch = template(`(opts) => fetch(URI, opts)`.concat(memberExpression)); + parentPath.replaceWithMultiple( + buildFetch({ + URI: t.stringLiteral(value), + }), + ); + } + } else { + parentPath.parentPath.remove(); + } + }; - if (value) { - if (isValueHaveArgs(value)) { - const buildFetch = template(`(PARAM) => fetch(URI, opts)`); - parentPath.replaceWithMultiple( - buildFetch({ - PARAM: t.objectPattern( - value - .split("/") - .filter((v) => v.startsWith(":")) - .map((p) => { - const id = t.identifier(p.replace(":", "")); - return t.objectProperty(id, id, false, true); - }) - .concat([t.restElement(t.identifier("opts"))]), - ), - URI: t.templateLiteral( - value - .replace(/:\w+/g, "::::") - .split("::::") - .map((v, i, a) => t.templateElement({ raw: v, cooked: v }, i + 1 === a.length)), - value - .split("/") - .filter((v) => v.startsWith(":")) - .map((v) => t.identifier(v.replace(":", ""))), - ), - }), - ); - } else { - const buildFetch = template(`(opts) => fetch(URI, opts)`); - parentPath.replaceWithMultiple( - buildFetch({ - URI: t.stringLiteral(value), - }), - ); - } - } else { - parentPath.parentPath.remove(); - } - }); -}); + (paths || []).forEach(transform("default")); + (fetchText || []).forEach(transform("fetchText")); + /** + * @todo {https://github.com/r17x/fetch.macro/issues/22} + * - [ ] fetchJson + * - [ ] fetchBlob + * - [ ] fetchArrayBuffer + * - [ ] fetchFormData + */ + }, +); diff --git a/tests/fetch.js b/tests/fetch.js index 76bd114c..2db42d6e 100644 --- a/tests/fetch.js +++ b/tests/fetch.js @@ -68,4 +68,16 @@ createTests("fetch", [ `, output: "", }, + // fetchText + { + title: "fetchText with url params", + code: ` + import {fetchText} from '../src/fetch.macro' + const fetchProject = fetchText\`/api/v1/user/:id/project/:projectId/:others\`; + `, + output: ` + const fetchProject = ({ id, projectId, others, ...opts }) => + fetch(\`/api/v1/user/\${id}/project/\${projectId}/\${others}\`, opts).then((r) => r.text()); + `, + }, ]);