Skip to content
This repository was archived by the owner on May 10, 2021. It is now read-only.

Commit 8515dd7

Browse files
copy dynamic import chunks to functions dirs (#174)
1 parent 13ffd05 commit 8515dd7

File tree

8 files changed

+277
-2
lines changed

8 files changed

+277
-2
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const { join } = require("path");
2+
const { copySync, readdirSync } = require("fs-extra");
3+
const { logTitle } = require("../helpers/logger");
4+
const { NEXT_DIST_DIR } = require("../config");
5+
6+
// Check if there are dynamic import chunks and copy to the necessary function dir
7+
const copyDynamicImportChunks = (functionPath) => {
8+
const chunksPath = join(NEXT_DIST_DIR, "serverless");
9+
const files = readdirSync(chunksPath);
10+
const chunkRegex = new RegExp(/^(\.?[-_$~A-Z0-9a-z]+){1,}\.js$/g);
11+
const excludeFiles = ["init-server.js.js", "on-error-server.js.js"];
12+
files.forEach((file) => {
13+
if (!excludeFiles.includes(file) && chunkRegex.test(file)) {
14+
logTitle("💼 Copying dynamic import chunks to", functionPath);
15+
copySync(join(chunksPath, file), join(functionPath, file), {
16+
overwrite: false,
17+
errorOnExist: true,
18+
});
19+
}
20+
});
21+
};
22+
23+
module.exports = copyDynamicImportChunks;

lib/helpers/setupNetlifyFunctionForPage.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
FUNCTION_TEMPLATE_PATH,
77
} = require("../config");
88
const getNetlifyFunctionName = require("./getNetlifyFunctionName");
9+
const copyDynamicImportChunks = require("./copyDynamicImportChunks");
910
const { logItem } = require("./logger");
1011

1112
// Create a Netlify Function for the page with the given file path
@@ -47,8 +48,11 @@ const setupNetlifyFunctionForPage = ({
4748
});
4849
});
4950

51+
// Copy any dynamic import chunks
52+
copyDynamicImportChunks(functionDirectory);
53+
5054
// Copy page
51-
const nextPageCopyPath = join(functionDirectory, "nextPage.js");
55+
const nextPageCopyPath = join(functionDirectory, "nextPage", "index.js");
5256
copySync(join(NEXT_DIST_DIR, "serverless", filePath), nextPageCopyPath, {
5357
overwrite: false,
5458
errorOnExist: true,

tests/configurableDirs.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const FUNCTIONS_DIR = "my-functions";
1616
const PUBLISH_DIR = "my-publish";
1717

1818
// Capture the output to verify successful build
19-
let buildOutput;
19+
let runOutput;
2020

2121
beforeAll(
2222
async () => {

tests/dynamicImports.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Test next-on-netlify when config is set from a function in next.config.js
2+
// See: https://github.com/netlify/next-on-netlify/issues/25
3+
4+
const { parse, join } = require("path");
5+
const { existsSync, readdirSync, readFileSync } = require("fs-extra");
6+
const buildNextApp = require("./helpers/buildNextApp");
7+
8+
// The name of this test file (without extension)
9+
const FILENAME = parse(__filename).name;
10+
11+
// The directory which will be used for testing.
12+
// We simulate a NextJS app within that directory, with pages, and a
13+
// package.json file.
14+
const PROJECT_PATH = join(__dirname, "builds", FILENAME);
15+
16+
// Capture the output to verify successful build
17+
let buildOutput;
18+
19+
beforeAll(
20+
async () => {
21+
buildOutput = await buildNextApp()
22+
.forTest(__filename)
23+
.withPages("pages-dynamic-imports")
24+
.withNextConfig("next.config.js-est")
25+
.withPackageJson("package.json")
26+
.withFile("components/Header.js", join("components", "Header.js"))
27+
.build();
28+
},
29+
// time out after 180 seconds
30+
180 * 1000
31+
);
32+
33+
describe("next-on-netlify", () => {
34+
const functionsDir = join(PROJECT_PATH, "out_functions");
35+
36+
test("builds successfully", () => {
37+
expect(buildOutput).toMatch("Next on Netlify");
38+
expect(buildOutput).toMatch("Success! All done!");
39+
});
40+
41+
test("copies chunk files to ", () => {
42+
const functionFiles = readdirSync(join(functionsDir, "next_index"));
43+
const chunkRegex = new RegExp(/(\.?[-_$~A-Z0-9a-z]+){1,}\.js$/g);
44+
let chunkFileExists;
45+
functionFiles.forEach((file) => {
46+
chunkFileExists = chunkRegex.test(file);
47+
});
48+
expect(chunkFileExists).toBe(true);
49+
});
50+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Header() {
2+
return <h1>header</h1>;
3+
}

tests/fixtures/next.config.js-est

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
target: "experimental-serverless-trace",
3+
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import Link from "next/link";
2+
import dynamic from "next/dynamic";
3+
const Header = dynamic(
4+
() => import(/* webpackChunkName: 'header' */ "../../components/Header"),
5+
{ ssr: true }
6+
);
7+
8+
const Show = ({ show }) => (
9+
<div>
10+
<Header />
11+
<p>
12+
This page uses getInitialProps() to fetch the show with the ID provided in
13+
the URL: /shows/:id
14+
<br />
15+
Refresh the page to see server-side rendering in action.
16+
<br />
17+
You can also try changing the ID to any other number between 1-10000.
18+
</p>
19+
20+
<hr />
21+
22+
<h1>Show #{show.id}</h1>
23+
<p>{show.name}</p>
24+
25+
<hr />
26+
27+
<Link href="/">
28+
<a>Go back home</a>
29+
</Link>
30+
</div>
31+
);
32+
33+
export const getServerSideProps = async ({ params }) => {
34+
const res = await fetch("https://api.tvmaze.com/shows/42");
35+
const data = await res.json();
36+
37+
return {
38+
props: {
39+
show: data,
40+
},
41+
};
42+
};
43+
44+
export default Show;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import Link from "next/link";
2+
import dynamic from "next/dynamic";
3+
const Header = dynamic(
4+
() => import(/* webpackChunkName: 'header' */ "../components/Header"),
5+
{ ssr: true }
6+
);
7+
8+
const Index = ({ shows }) => (
9+
<div>
10+
<img
11+
src="/next-on-netlify.png"
12+
alt="NextJS on Netlify Banner"
13+
style={{ maxWidth: "100%" }}
14+
/>
15+
16+
<Header />
17+
18+
<p>
19+
This is a demo of a NextJS application with Server-Side Rendering (SSR).
20+
<br />
21+
It is hosted on Netlify.
22+
<br />
23+
Server-side rendering is handled by Netlify Functions.
24+
<br />
25+
Minimal configuration is required.
26+
<br />
27+
Everything is handled by the{" "}
28+
<a href="https://www.npmjs.com/package/next-on-netlify">
29+
next-on-netlify
30+
</a>{" "}
31+
npm package.
32+
</p>
33+
34+
<h1>1. Server-Side Rendering Made Easy</h1>
35+
<p>
36+
This page is server-side rendered.
37+
<br />
38+
It fetches a random list of five TV shows from the TVmaze REST API.
39+
<br />
40+
Refresh this page to see it change.
41+
</p>
42+
43+
<ul>
44+
{shows.map(({ id, name }) => (
45+
<li key={id}>
46+
<Link href="/shows/[id]" as={`/shows/${id}`}>
47+
<a>
48+
#{id}: {name}
49+
</a>
50+
</Link>
51+
</li>
52+
))}
53+
</ul>
54+
55+
<h1>2. Full Support for Dynamic Pages</h1>
56+
<p>
57+
Dynamic pages, introduced in NextJS 9.2, are fully supported.
58+
<br />
59+
Click on a show to check out a server-side rendered page with dynamic
60+
routing (/shows/:id).
61+
</p>
62+
63+
<ul>
64+
{shows.slice(0, 3).map(({ id, name }) => (
65+
<li key={id}>
66+
<Link href="/shows/[id]" as={`/shows/${id}`}>
67+
<a>
68+
#{id}: {name}
69+
</a>
70+
</Link>
71+
</li>
72+
))}
73+
</ul>
74+
75+
<h1>3. Catch-All Routes? Included ✔</h1>
76+
<p>
77+
You can even take advantage of{" "}
78+
<a href="https://nextjs.org/docs/routing/dynamic-routes#catch-all-routes">
79+
NextJS' catch-all routes feature
80+
</a>
81+
.
82+
<br />
83+
Here are three examples:
84+
</p>
85+
<ul>
86+
<li>
87+
<Link href="/shows/[...params]" as={`/shows/73/whatever/path/you/want`}>
88+
<a>/shows/73/whatever/path/you/want</a>
89+
</Link>
90+
</li>
91+
<li>
92+
<Link href="/shows/[...params]" as={`/shows/94/whatever/path/you`}>
93+
<a>/shows/94/whatever/path/you</a>
94+
</Link>
95+
</li>
96+
<li>
97+
<Link href="/shows/[...params]" as={`/shows/106/whatever/path`}>
98+
<a>/shows/106/whatever/path</a>
99+
</Link>
100+
</li>
101+
</ul>
102+
103+
<h1>4. Static Pages Stay Static</h1>
104+
<p>
105+
next-on-netlify automatically determines which pages are dynamic and which
106+
ones are static.
107+
<br />
108+
Only dynamic pages are server-side rendered.
109+
<br />
110+
Static pages are pre-rendered and served directly by Netlify's CDN.
111+
</p>
112+
113+
<ul>
114+
<li>
115+
<Link href="/static">
116+
<a>Static NextJS page: /static</a>
117+
</Link>
118+
</li>
119+
<li>
120+
<Link href="/static/[id]" as="/static/123456789">
121+
<a>Static NextJS page with dynamic routing: /static/:id</a>
122+
</Link>
123+
</li>
124+
</ul>
125+
126+
<h1>Want to Learn More?</h1>
127+
<p>
128+
Check out the{" "}
129+
<a href="https://github.com/FinnWoelm/next-on-netlify-demo">
130+
source code on GitHub
131+
</a>
132+
.
133+
</p>
134+
</div>
135+
);
136+
137+
Index.getInitialProps = async function () {
138+
// Set a random page between 1 and 100
139+
const randomPage = Math.floor(Math.random() * 100) + 1;
140+
141+
// Get the data
142+
const res = await fetch(`https://api.tvmaze.com/shows?page=${randomPage}`);
143+
const data = await res.json();
144+
145+
return { shows: data.slice(0, 5) };
146+
};
147+
148+
export default Index;

0 commit comments

Comments
 (0)