Skip to content

Commit 43d875d

Browse files
committed
Initial commit of code
1 parent 0733ae1 commit 43d875d

10 files changed

+610
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/build/
2+
/node_modules/

README.md

+68
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,70 @@
11
# compute-js-static-publish
22
Static Publisher for Compute@Edge JavaScript
3+
4+
Easily run your React application generated with [`create-react-app`](https://create-react-app.dev/) on
5+
a [Compute@Edge service](https://developer.fastly.com/learning/compute/javascript/).
6+
7+
`create-react-app` provides a simple-to-use platform for getting started developing React
8+
applications. When you need to deploy your app to the internet, why not deploy it to Fastly's
9+
blazing-fast Edge Computing Platform?
10+
11+
## How it works
12+
13+
1. Use Create React App to create your application.
14+
15+
```shell
16+
npx create-react-app my-app
17+
```
18+
19+
2. Work on your app as normal.
20+
21+
```shell
22+
cd my-app
23+
npm start
24+
```
25+
26+
3. When you're ready to deploy to Fastly, build your production bundle, then run `compute-js-static-publish`.
27+
28+
```shell
29+
npm run build # create-react-app's build command
30+
npx @fastly/compute-js-static-publish
31+
```
32+
33+
This will make a `compute-js` subfolder and initialize it as a Compute@Edge JavaScript application.
34+
It will add a default `./src/index.js` file that serves the static files from your production bundle.
35+
36+
4. Run your application using [Fastly's local development server](https://developer.fastly.com/learning/compute/testing/#running-a-local-testing-server).
37+
38+
```shell
39+
cd ./compute-js
40+
npm install
41+
fastly compute serve
42+
```
43+
44+
5. When you're ready to go live, [deploy it to your Compute@Edge service](https://developer.fastly.com/reference/cli/compute/publish/).
45+
46+
```shell
47+
fastly compute publish
48+
```
49+
50+
6. Each time you build your Compute@Edge project, `compute-js-static-publish` will run a process that scans your `./build`
51+
directory for changes and generates a new `./src/statics.js` file.
52+
53+
You're free to modify the `./src/index.js` handler to suit your needs, such as to add your own API endpoints.
54+
This framework will not touch that file after creation.
55+
56+
## Options
57+
58+
```shell
59+
npx @fastly/compute-js-static-publish --public-path=./build --static-path=./build/static --output=./compute-js --spa
60+
```
61+
62+
| Option | Default | Description |
63+
|-----------------|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
64+
| `--public-path` | `./build` | The directory that contains the application's public files. All files in this directory and subdirectories will be served by your Compute@Edge handler. |
65+
| `--static-path` | `./build/static` | A subdirectory of `--public-path` that contains the application's static files. The files in this directory and subdirectories will be served with a 1 year cache. |
66+
| `--output` | `./compute-js` | The directory in which to create the Compute@Edge application. |
67+
| `--spa` | `false` | If true, then the Compute@Edge handler will serve `<public-path>/index.html` when the requested file does not match any of the files in `<public-path>`. Useful for apps that use [client-side routing](https://create-react-app.dev/docs/deployment#serving-apps-with-client-side-routing). |
68+
69+
The path and spa flag will be written to a `static-publish.json` file, and is referenced by the `compute-js-build-static-loader`
70+
command that is run on each `build` of the Compute@Edge project (this runs as a part of the `prebuild` script in `package.json`).

package-lock.json

+54
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@fastly/compute-js-static-publish",
3+
"type": "module",
4+
"version": "1.0.0",
5+
"description": "Static Publisher for Compute@Edge JavaScript",
6+
"main": "./build/index.js",
7+
"scripts": {
8+
"prepack": "npm run build",
9+
"build": "tsc",
10+
"test": "echo \"Error: no test specified\" && exit 1"
11+
},
12+
"bin": {
13+
"compute-js-static-publish": "./build/index.js",
14+
"compute-js-build-static-loader": "./build/build-static.js"
15+
},
16+
"repository": {
17+
"type": "git",
18+
"url": "git+https://github.com/fastly/compute-js-static-publish.git"
19+
},
20+
"keywords": [
21+
"compute-js",
22+
"javascript",
23+
"static",
24+
"serve"
25+
],
26+
"author": "Katsuyuki Omuro <[email protected]>",
27+
"license": "MIT",
28+
"bugs": {
29+
"url": "https://github.com/fastly/compute-js-static-publish/issues"
30+
},
31+
"homepage": "https://github.com/fastly/compute-js-static-publish#readme",
32+
"dependencies": {
33+
"typescript": "^4.7.4"
34+
},
35+
"devDependencies": {
36+
"@types/node": "^18.0.0"
37+
}
38+
}

resources/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Resources
2+
3+
The files in this directory are copied into the output folder.

resources/index.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/// <reference types="@fastly/js-compute" />
2+
3+
import { Router } from '@fastly/expressly';
4+
import { assets, isSpa } from './statics';
5+
6+
const router = new Router();
7+
8+
router.get("*", (req, res) => {
9+
const path = req.urlObj.pathname !== '/' ? req.urlObj.pathname : '/index.html';
10+
const staticFile = assets[path];
11+
if(staticFile == null) {
12+
return;
13+
}
14+
15+
// Aggressive caching for static files, and no caching for everything else.
16+
// https://create-react-app.dev/docs/production-build/#static-file-caching
17+
const headers = {
18+
'Cache-Control': staticFile.isStatic ? 'max-age=31536000' : 'no-cache',
19+
};
20+
if(staticFile.contentType != null) {
21+
headers['Content-Type'] = staticFile.contentType;
22+
}
23+
res.send(new Response(staticFile.content, {
24+
status: 200,
25+
headers,
26+
}));
27+
});
28+
29+
// TODO: If you need to handle any API routes, add them here.
30+
// router.get("/api/endpoint", (req, res) => {
31+
// res.send("foo");
32+
// });
33+
34+
// If this is a SPA, then return index.html for HTML requests
35+
router.get("*", (req, res) => {
36+
if(!isSpa || !(req.headers.get('Accept') ?? '').split(',').includes('text/html')) {
37+
return;
38+
}
39+
40+
const staticFile = assets['/index.html'];
41+
res.send(new Response(staticFile.content, {
42+
status: 200,
43+
headers: {
44+
'Cache-Control': 'no-cache',
45+
'Content-Type': 'text/html',
46+
}
47+
}));
48+
});
49+
50+
router.all("*", (req, res) => {
51+
res.send(new Response("404 Not Found", {
52+
status: 404,
53+
headers: {
54+
'Content-Type': 'text/plain',
55+
},
56+
}));
57+
});
58+
59+
router.listen();

resources/webpack.config.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
const path = require("path");
2+
const webpack = require("webpack");
3+
const { ProvidePlugin } = webpack;
4+
5+
let config;
6+
try {
7+
config = require("./static-publish.json");
8+
} catch {
9+
console.error('Error loading static-publish.json');
10+
process.exit(1);
11+
}
12+
13+
const buildDir = path.resolve(config.buildDir);
14+
15+
module.exports = {
16+
entry: "./src/index.js",
17+
optimization: {
18+
minimize: false
19+
},
20+
target: "webworker",
21+
output: {
22+
filename: "index.js",
23+
path: path.resolve(__dirname, "bin"),
24+
libraryTarget: "this",
25+
},
26+
module: {
27+
// Asset modules are modules that allow the use asset files (fonts, icons, etc)
28+
// without additional configuration or dependencies.
29+
rules: [
30+
// asset/source exports the source code of the asset.
31+
// Usage: e.g., import notFoundPage from "./page_404.html"
32+
{
33+
test: (file) => {
34+
if(!file.startsWith(buildDir + '/')) {
35+
return false;
36+
}
37+
return /\.(txt|htm(l)?|xml|json|map|js|css|svg)/.test(file);
38+
},
39+
type: "asset/source",
40+
},
41+
// asset/inline exports the raw bytes of the asset.
42+
// We base64 encode them here
43+
{
44+
test: (file) => {
45+
if(!file.startsWith(buildDir + '/')) {
46+
return false;
47+
}
48+
return /\.(bmp|png|gif|jp(e)?g|ico|tif(f)?|aac|mp3|mp4|mpeg|webm|pdf|tar|zip|eot|otf|ttf)/.test(file);
49+
},
50+
type: "asset/inline",
51+
generator: {
52+
/**
53+
* @param {Buffer} content
54+
* @returns {string}
55+
*/
56+
dataUrl: content => {
57+
return content.toString('base64');
58+
},
59+
}
60+
},
61+
],
62+
},
63+
plugins: [
64+
// Polyfills go here.
65+
// Used for, e.g., any cross-platform WHATWG,
66+
// or core nodejs modules needed for your application.
67+
new ProvidePlugin({
68+
Buffer: [ "buffer", "Buffer" ],
69+
}),
70+
],
71+
resolve: {
72+
fallback: {
73+
"buffer": require.resolve("buffer/"),
74+
},
75+
},
76+
};

0 commit comments

Comments
 (0)