diff --git a/.changeset/five-peas-brake.md b/.changeset/five-peas-brake.md new file mode 100644 index 000000000..9e6391391 --- /dev/null +++ b/.changeset/five-peas-brake.md @@ -0,0 +1,5 @@ +--- +'preact-cli': minor +--- + +Supports consuming "proxy" from package.json to proxy API requests in watch mode diff --git a/packages/cli/lib/lib/webpack/webpack-client-config.js b/packages/cli/lib/lib/webpack/webpack-client-config.js index 646e76566..dac17d00d 100644 --- a/packages/cli/lib/lib/webpack/webpack-client-config.js +++ b/packages/cli/lib/lib/webpack/webpack-client-config.js @@ -278,6 +278,43 @@ function isProd(env) { return prodConfig; } +function setupProxy(target) { + if (!target) { + return; + } + + const errorTemplate = warn => + `Invalid proxy configuration in package.json. ${warn} Skipping and using default.`; + + if (typeof target !== 'string') { + warn(errorTemplate('If provided, "proxy" must be a string.')); + return; + } else if (!/https?:\/\//.test(target)) { + warn( + errorTemplate( + 'If provided, "proxy" must start with "http://" or "https://".' + ) + ); + return; + } + + return { + target, + logLevel: 'warn', + context: (pathname, req) => { + return ( + req.method != 'GET' || + (!(/^\/?assets/.test(pathname) || pathname.startsWith('/ws')) && + req.headers.accept.indexOf('html') === -1) + ); + }, + secure: false, + changeOrigin: true, + ws: true, + xfwd: true, + }; +} + /** * @returns {import('webpack').Configuration} */ @@ -315,6 +352,7 @@ function isDev(env) { logging: 'none', overlay: false, }, + proxy: setupProxy(env.pkg.proxy), }, }; } diff --git a/packages/cli/tests/build.test.js b/packages/cli/tests/build.test.js index 7405ce16e..fe172d934 100644 --- a/packages/cli/tests/build.test.js +++ b/packages/cli/tests/build.test.js @@ -73,7 +73,9 @@ describe('preact build', () => { // The tsconfig.json in the template covers the test directory, // so TS will error out if it can't find even test-only module definitions shell.cd(dir); - shell.exec('npm i @types/enzyme enzyme-adapter-preact-pure'); + //shell.exec('npm i @types/enzyme@3.10.11 enzyme-adapter-preact-pure'); + // Remove when https://github.com/preactjs/enzyme-adapter-preact-pure/issues/161 is resolved + shell.exec('rm tsconfig.json'); expect(() => build(dir)).not.toThrow(); }); diff --git a/packages/cli/tests/server.js b/packages/cli/tests/server.js index b0fbef8a3..abbae9672 100644 --- a/packages/cli/tests/server.js +++ b/packages/cli/tests/server.js @@ -6,5 +6,10 @@ exports.getServer = (dir, port = 3000) => { maxAge: 0, single: true, }; - return polka().use(sirv(dir, opts)).listen(port); + return polka() + .use(sirv(dir, opts)) + .get('/proxied/request', (_req, res) => { + res.end('Hello World!'); + }) + .listen(port); }; diff --git a/packages/cli/tests/subjects/proxy/index.js b/packages/cli/tests/subjects/proxy/index.js new file mode 100644 index 000000000..e154e7db6 --- /dev/null +++ b/packages/cli/tests/subjects/proxy/index.js @@ -0,0 +1,14 @@ +import { h } from 'preact'; +import { useEffect, useState } from 'preact/hooks'; + +export default () => { + const [val, setVal] = useState(''); + + useEffect(() => { + fetch('/proxied/request') + .then(res => res.text()) + .then(data => setVal(data)); + }, []); + + return

Data retrieved from proxied server: {val}

; +}; diff --git a/packages/cli/tests/subjects/proxy/package.json b/packages/cli/tests/subjects/proxy/package.json new file mode 100644 index 000000000..090ae9420 --- /dev/null +++ b/packages/cli/tests/subjects/proxy/package.json @@ -0,0 +1,5 @@ +{ + "private": true, + "name": "preact-proxy", + "proxy": "http://localhost:8086" +} diff --git a/packages/cli/tests/watch.test.js b/packages/cli/tests/watch.test.js index f7ac825fc..49143d9ed 100644 --- a/packages/cli/tests/watch.test.js +++ b/packages/cli/tests/watch.test.js @@ -3,6 +3,8 @@ const { resolve } = require('path'); const startChrome = require('./lib/chrome'); const { create, watch } = require('./lib/cli'); const { determinePort } = require('../lib/commands/watch'); +const { subject } = require('./lib/output'); +const { getServer } = require('./server'); const { loadPage, waitUntilExpression } = startChrome; let chrome, server; @@ -62,6 +64,23 @@ describe('preact', () => { server.close(); }); + + it('should proxy requests when "proxy" exists in package.json', async () => { + const api = getServer('', 8086); + let app = await subject('proxy'); + + server = await watch(app, 8087); + + let page = await loadPage(chrome, 'http://127.0.0.1:8087/'); + + await waitUntilExpression( + page, + `document.querySelector('h1').innerText === 'Data retrieved from proxied server: Hello World!'` + ); + + server.close(); + api.server.close(); + }); }); describe('should determine the correct port', () => {