Skip to content

Commit a56d904

Browse files
authored
Fix/prerender urls esm (#1667)
* fix: Allows prerender-urls.js to be esm once again * test: Adds test suite for config files * docs: Adding changeset * test: Removing negative config file tests * fix: Yarn PnP throws a completely different message for _reasons_ * docs: Adding additional prerenderUrls example to readme * refactor: Make config file handling consistent * test: Improved config format test suite * docs: Ensuring file labels are consistent
1 parent aea6fed commit a56d904

26 files changed

+1526
-1335
lines changed

.changeset/dirty-socks-invite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'preact-cli': patch
3+
---
4+
5+
Allows users to author prerender-urls.js as ESM once again

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,9 @@ preact build --prerenderUrls src/prerender-urls.json
266266
If a static JSON file is too restrictive, you may want to provide a javascript file that exports your routes instead.
267267
Routes can be exported as a JSON string or an object and can optionally be returned from a function.
268268

269+
> `prerender-urls.js`
270+
269271
```js
270-
// prerender-urls.js
271272
module.exports = [
272273
{
273274
url: '/',
@@ -279,6 +280,22 @@ module.exports = [
279280
];
280281
```
281282

283+
> `prerender-urls.js`
284+
285+
```js
286+
export default () => {
287+
return [
288+
{
289+
url: '/',
290+
title: 'Homepage',
291+
},
292+
{
293+
url: '/route/random',
294+
},
295+
];
296+
};
297+
```
298+
282299
#### Template
283300

284301
A template is used to render your page by [EJS](https://ejs.co/).

packages/cli/jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ module.exports = {
3131

3232
// This option allows use of a custom test runner
3333
// testRunner: "jest-circus/runner",
34+
35+
// TODO: Restored in #1667, remove when upgrading Jest
36+
testEnvironment: 'node',
3437
};

packages/cli/lib/lib/webpack/render-html-plugin.js

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ const HtmlWebpackExcludeAssetsPlugin = require('html-webpack-exclude-assets-plug
55
const HtmlWebpackPlugin = require('html-webpack-plugin');
66
const prerender = require('./prerender');
77
const createLoadManifest = require('./create-load-manifest');
8-
const { warn } = require('../../util');
9-
const { info } = require('../../util');
8+
const { esmImport, tryResolveConfig, warn } = require('../../util');
109

1110
const PREACT_FALLBACK_URL = '/200.html';
1211

@@ -107,21 +106,27 @@ module.exports = async function (config) {
107106
let pages = [{ url: '/' }];
108107

109108
if (config.prerenderUrls) {
110-
if (existsSync(resolve(cwd, config.prerenderUrls))) {
109+
const prerenderUrls = tryResolveConfig(
110+
cwd,
111+
config.prerenderUrls,
112+
config.prerenderUrls === 'prerender-urls.json',
113+
config.verbose
114+
);
115+
116+
if (prerenderUrls) {
111117
try {
112-
let result = require(resolve(cwd, config.prerenderUrls));
118+
let result = esmImport(prerenderUrls);
119+
113120
if (typeof result.default !== 'undefined') {
114-
result = result.default();
121+
result = result.default;
115122
}
116123
if (typeof result === 'function') {
117-
info(`Fetching URLs from ${config.prerenderUrls}`);
118124
result = await result();
119-
info(`Fetched URLs from ${config.prerenderUrls}`);
120125
}
121126
if (typeof result === 'string') {
122127
result = JSON.parse(result);
123128
}
124-
if (result instanceof Array) {
129+
if (Array.isArray(result)) {
125130
pages = result;
126131
}
127132
} catch (error) {
@@ -131,17 +136,6 @@ module.exports = async function (config) {
131136
}`
132137
);
133138
}
134-
// don't warn if the default file doesn't exist
135-
} else if (
136-
config.prerenderUrls !== 'prerender-urls.json' ||
137-
config.verbose
138-
) {
139-
warn(
140-
`prerenderUrls file (${resolve(
141-
cwd,
142-
config.prerenderUrls
143-
)}) doesn't exist, using default!`
144-
);
145139
}
146140
}
147141
/**

packages/cli/lib/lib/webpack/transform-config.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { resolve } = require('path');
22
const webpack = require('webpack');
33
const { stat } = require('fs').promises;
4-
const { error } = require('../../util');
4+
const { error, esmImport, tryResolveConfig, warn } = require('../../util');
55

66
const FILE = 'preact.config';
77
const EXTENSIONS = ['js', 'json'];
@@ -95,24 +95,26 @@ module.exports = async function (env, webpackConfig, isServer = false) {
9595
env.config !== 'preact.config.js'
9696
? { configFile: env.config, isDefault: false }
9797
: await findConfig(env);
98-
env.config = configFile;
99-
let myConfig = resolve(env.cwd, env.config);
10098

101-
try {
102-
await stat(myConfig);
103-
} catch (e) {
104-
if (isDefault) return;
105-
throw new Error(
106-
`preact-cli config could not be loaded!\nFile ${env.config} not found.`
107-
);
108-
}
99+
const cliConfig = tryResolveConfig(
100+
env.cwd,
101+
configFile,
102+
isDefault,
103+
env.verbose
104+
);
109105

110-
let m = require('esm')(module)(myConfig);
106+
if (!cliConfig) return;
111107

112-
// The line above results in an empty object w/ Jest,
113-
// so we need to do the following in order to load it:
114-
if (Object.keys(m).length === 0) {
115-
m = require(myConfig);
108+
let m;
109+
try {
110+
m = esmImport(cliConfig);
111+
} catch (error) {
112+
warn(
113+
`Failed to load preact-cli config file, using default!\n${
114+
env.verbose ? error.stack : error.message
115+
}`
116+
);
117+
return;
116118
}
117119

118120
const transformers = parseConfig((m && m.default) || m);
@@ -131,7 +133,7 @@ module.exports = async function (env, webpackConfig, isServer = false) {
131133
options
132134
);
133135
} catch (err) {
134-
throw new Error((`Error at ${myConfig}: \n` + err && err.stack) || err);
136+
throw new Error((`Error at ${cliConfig}: \n` + err && err.stack) || err);
135137
}
136138
}
137139
};

packages/cli/lib/util.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ exports.info = function (text, code) {
3030
code && process.exit(code);
3131
};
3232

33-
exports.warn = function (text, code) {
33+
const warn = (exports.warn = function (text, code) {
3434
process.stdout.write(symbols.warning + yellow(' WARN ') + text + '\n');
3535
code && process.exit(code);
36-
};
36+
});
3737

3838
exports.error = function (text, code = 1) {
3939
process.stderr.write(symbols.error + red(' ERROR ') + text + '\n');
@@ -56,6 +56,8 @@ exports.toBool = function (val) {
5656
return val === void 0 || (val === 'false' ? false : val);
5757
};
5858

59+
exports.esmImport = require('esm')(module);
60+
5961
/**
6062
* Taken from: https://github.com/preactjs/wmr/blob/3401a9bfa6491d25108ad68688c067a7e17d0de5/packages/wmr/src/lib/net-utils.js#L4-Ll4
6163
* Check if a port is free
@@ -78,3 +80,12 @@ exports.isPortFree = async function (port) {
7880
return false;
7981
}
8082
};
83+
84+
exports.tryResolveConfig = function (cwd, file, isDefault, verbose) {
85+
const path = resolve(cwd, file);
86+
if (existsSync(path)) {
87+
return path;
88+
} else if (!isDefault || verbose) {
89+
warn(`${resolve(cwd, file)} doesn't exist, using default!`);
90+
}
91+
};

packages/cli/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
"homepage": "https://github.com/preactjs/preact-cli",
3535
"devDependencies": {
3636
"@types/express": "^4.17.13",
37-
"@types/jest": "^27.4.0",
37+
"@types/jest": "^24.9.1",
3838
"html-looks-like": "^1.0.2",
39-
"jest": "^27.0.1",
39+
"jest": "^24.9.0",
4040
"less": "^4.1.1",
4141
"less-loader": "^7.3.0",
4242
"ncp": "^2.0.0",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const { join } = require('path');
2+
const { access } = require('fs').promises;
3+
const { build } = require('./lib/cli');
4+
const { subject } = require('./lib/output');
5+
6+
const formats = ['cjs', 'esm'];
7+
8+
const prerenderUrlFiles = [
9+
'array.js',
10+
'stringified-array.js',
11+
'function-returning-array.js',
12+
'function-returning-stringified-array.js',
13+
];
14+
15+
const preactConfigFiles = ['function.js', 'object.js'];
16+
17+
describe('config files', () => {
18+
describe('prerender-urls', () => {
19+
it(`should load the 'prerender-urls.json' file`, async () => {
20+
let dir = await subject('multiple-config-files');
21+
22+
await build(dir);
23+
24+
expect(await access(join(dir, 'build/index.html'))).toBeUndefined();
25+
expect(
26+
await access(join(dir, 'build/custom/index.html'))
27+
).toBeUndefined();
28+
});
29+
30+
formats.forEach(moduleFormat => {
31+
prerenderUrlFiles.forEach(dataFormat => {
32+
it(`should load the '${dataFormat}' file in ${moduleFormat}`, async () => {
33+
let dir = await subject('multiple-config-files');
34+
35+
await build(dir, {
36+
prerenderUrls: `prerenderUrls/${moduleFormat}/${dataFormat}`,
37+
});
38+
39+
expect(await access(join(dir, 'build/index.html'))).toBeUndefined();
40+
expect(
41+
await access(join(dir, 'build/custom/index.html'))
42+
).toBeUndefined();
43+
});
44+
});
45+
});
46+
});
47+
48+
describe('preact.config', () => {
49+
formats.forEach(moduleFormat => {
50+
preactConfigFiles.forEach(dataFormat => {
51+
it(`should load the '${dataFormat}' file in ${moduleFormat}`, async () => {
52+
let dir = await subject('multiple-config-files');
53+
54+
await build(dir, {
55+
config: `preactConfig/${moduleFormat}/${dataFormat}`,
56+
});
57+
58+
expect(await access(join(dir, 'build/bundle.js'))).toBeUndefined();
59+
});
60+
});
61+
});
62+
});
63+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { h, Component } from 'preact';
2+
import { Router } from 'preact-router';
3+
import Home from './routes/home';
4+
5+
export default class App extends Component {
6+
handleRoute = e => {
7+
this.currentUrl = e.url;
8+
};
9+
10+
render(props) {
11+
return (
12+
<div id="app">
13+
<Router url={props.url} onChange={this.handleRoute} {...props}>
14+
<Home path="/" />
15+
</Router>
16+
</div>
17+
);
18+
}
19+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"private": true,
3+
"name": "preact-config"
4+
}

0 commit comments

Comments
 (0)