Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Next] fetch support #1563

Merged
merged 4 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions docs/src/pages/guides/data-fetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@ console.log(data);

## Using `fetch()` outside of Astro Components

If you want to use `fetch()` in a non-astro component, use the [`node-fetch`](https://github.com/node-fetch/node-fetch) library:
If you want to use `fetch()` in a non-astro component, it is also globally available:

```tsx
// Movies.tsx
import fetch from 'node-fetch';
import type { FunctionalComponent } from 'preact';
import { h } from 'preact';

Expand All @@ -54,11 +53,3 @@ const Movies: FunctionalComponent = () => {

export default Movies;
```

If you load a component using `node-fetch` [interactively](/core-concepts/component-hydration), with `client:load`, `client:visible`, etc., you'll need to either not use `node-fetch` or switch to an [isomorphic](https://en.wikipedia.org/wiki/Isomorphic_JavaScript) library that will run both at build time and on the client, as the [`node-fetch` README.md](https://github.com/node-fetch/node-fetch#motivation) recommends:

> Instead of implementing XMLHttpRequest in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native http to fetch API directly? Hence, node-fetch, minimal code for a window.fetch compatible API on Node.js runtime.
>
> See Jason Miller's [isomorphic-unfetch](https://www.npmjs.com/package/isomorphic-unfetch) or Leonardo Quixada's [cross-fetch](https://github.com/lquixada/cross-fetch) for isomorphic usage (exports node-fetch for server-side, whatwg-fetch for client-side).

> Quoted from https://github.com/node-fetch/node-fetch#motivation
2 changes: 2 additions & 0 deletions packages/astro/src/runtime/vite/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import astroVitePlugin from './plugin-astro.js';
import astroPostprocessVitePlugin from './plugin-astro-postprocess.js';
import markdownVitePlugin from './plugin-markdown.js';
import jsxVitePlugin from './plugin-jsx.js';
import fetchVitePlugin from './plugin-fetch.js';
import { AstroDevServer } from '../../dev';

const require = createRequire(import.meta.url);
Expand Down Expand Up @@ -86,6 +87,7 @@ export async function loadViteConfig(
markdownVitePlugin({ config: astroConfig, devServer }),
jsxVitePlugin({ config: astroConfig, logging }),
astroPostprocessVitePlugin({ config: astroConfig, devServer }),
fetchVitePlugin(),
...plugins
],
publicDir: fileURLToPath(astroConfig.public),
Expand Down
58 changes: 58 additions & 0 deletions packages/astro/src/runtime/vite/plugin-fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Plugin } from 'vite';
import MagicString from 'magic-string';

// https://github.com/vitejs/vite/discussions/5109#discussioncomment-1450726
function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
if (options === undefined) {
return false
}
if (typeof options === 'boolean') {
return options
}
if (typeof options == 'object') {
return !!options.ssr
}
return false
}

// This matches any JS-like file (that we know of)
// See https://regex101.com/r/Cgofir/1
const SUPPORTED_FILES = /\.(astro|svelte|vue|[cm]?js|jsx|[cm]?ts|tsx)$/;
const DEFINE_FETCH = `import fetch from 'node-fetch';\n`;

export default function pluginFetch(): Plugin {
return {
name: '@astrojs/vite-plugin-fetch',
enforce: 'post',
async transform(code, id, opts) {
const ssr = isSSR(opts);

// If this isn't an SSR pass, `fetch` will already be available!
if (!ssr) {
return null;
}

// Only transform JS-like files
if (!id.match(SUPPORTED_FILES)) {
return null;
}

// Optimization: only run on probable matches
if (!code.includes('fetch')) {
return null;
}

const s = new MagicString(code);
s.prepend(DEFINE_FETCH);

const result = s.toString();

const map = s.generateMap({
source: id,
includeContent: true
});

return { code: result, map }
},
};
}
22 changes: 14 additions & 8 deletions packages/astro/test/fetch.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/**
* UNCOMMENT: add fetch() in component support
import { expect } from 'chai';
import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
Expand All @@ -11,14 +9,22 @@ before(async () => {
await fixture.build();
});


describe('Global Fetch', () => {
it('Is available in non-Astro components.', async () => {
it('Is available in Astro pages', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('#astro-page').text()).to.equal('function', 'Fetch supported in .astro page');
});
it('Is available in Astro components', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('#jsx').text()).to.equal('function');
expect($('#astro-component').text()).to.equal('function', 'Fetch supported in .astro components');
});
it('Is available in non-Astro components', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('#jsx').text()).to.equal('function', 'Fetch supported in .jsx');
expect($('#svelte').text()).to.equal('function', 'Fetch supported in .svelte');
expect($('#vue').text()).to.equal('function', 'Fetch supported in .vue');
});
});
*/

it.skip('is skipped', () => {});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<span id="astro-component">{typeof fetch}</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<span id="svelte">{ typeof fetch }</span>
16 changes: 16 additions & 0 deletions packages/astro/test/fixtures/fetch/src/components/VueComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<span id="vue">{{ type }}</span>
</template>

<script>
import { defineComponent } from 'vue'

export default defineComponent({
setup() {
return {
type: typeof fetch
}
}
})
</script>

13 changes: 10 additions & 3 deletions packages/astro/test/fixtures/fetch/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
---
import Child from '../components/Child.jsx';
import Test from '../components/AstroComponent.astro';
import JsxComponent from '../components/JsxComponent.jsx';
import SvelteComponent from '../components/SvelteComponent.svelte';
import VueComponent from '../components/VueComponent.vue';
---

<html lang="en">
<head>
<title>Global fetch</title>
</head>
<body>
<Child />
<span id="astro-page">{typeof fetch}</span>
<Test />
<JsxComponent />
<SvelteComponent />
<VueComponent />
</body>
</html>
</html>