Skip to content

Commit 47c650b

Browse files
committed
Initial Commit of new version
1 parent eee0b70 commit 47c650b

32 files changed

+1743
-446
lines changed

package-lock.json

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

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@fastly/compute-js-static-publish",
33
"type": "module",
4-
"version": "3.6.0",
4+
"version": "4.0.0-alpha.0",
55
"description": "Static Publisher for Compute@Edge JavaScript",
66
"main": "./build/index.js",
77
"types": "./build/index.d.ts",

resources/default-content-types.cjs

+38-38
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
const defaultContentTypes = [
22
// Text formats
3-
{ test: /.txt$/, type: 'text/plain', binary: false },
4-
{ test: /.htm(l)?$/, type: 'text/html', binary: false },
5-
{ test: /.xml$/, type: 'application/xml', binary: false },
6-
{ test: /.json$/, type: 'application/json', binary: false },
7-
{ test: /.map$/, type: 'application/json', binary: false },
8-
{ test: /.js$/, type: 'application/javascript', binary: false },
9-
{ test: /.css$/, type: 'text/css', binary: false },
10-
{ test: /.svg$/, type: 'image/svg+xml', binary: false },
3+
{ test: /.txt$/, contentType: 'text/plain', text: true },
4+
{ test: /.htm(l)?$/, contentType: 'text/html', text: true },
5+
{ test: /.xml$/, contentType: 'application/xml', text: true },
6+
{ test: /.json$/, contentType: 'application/json', text: true },
7+
{ test: /.map$/, contentType: 'application/json', text: true },
8+
{ test: /.js$/, contentType: 'application/javascript', text: true },
9+
{ test: /.css$/, contentType: 'text/css', text: true },
10+
{ test: /.svg$/, contentType: 'image/svg+xml', text: true },
1111

1212
// Binary formats
13-
{ test: /.bmp$/, type: 'image/bmp', binary: true },
14-
{ test: /.png$/, type: 'image/png', binary: true },
15-
{ test: /.gif$/, type: 'image/gif', binary: true },
16-
{ test: /.jp(e)?g$/, type: 'image/jpeg', binary: true },
17-
{ test: /.ico$/, type: 'image/vnd.microsoft.icon', binary: true },
18-
{ test: /.tif(f)?$/, type: 'image/png', binary: true },
19-
{ test: /.aac$/, type: 'audio/aac', binary: true },
20-
{ test: /.mp3$/, type: 'audio/mpeg', binary: true },
21-
{ test: /.avi$/, type: 'video/x-msvideo', binary: true },
22-
{ test: /.mp4$/, type: 'video/mp4', binary: true },
23-
{ test: /.mpeg$/, type: 'video/mpeg', binary: true },
24-
{ test: /.webm$/, type: 'video/webm', binary: true },
25-
{ test: /.pdf$/, type: 'application/pdf', binary: true },
26-
{ test: /.tar$/, type: 'application/x-tar', binary: true },
27-
{ test: /.zip$/, type: 'application/zip', binary: true },
28-
{ test: /.eot$/, type: 'application/vnd.ms-fontobject', binary: true },
29-
{ test: /.otf$/, type: 'font/otf', binary: true },
30-
{ test: /.ttf$/, type: 'font/ttf', binary: true },
31-
{ test: /.woff$/, type: 'font/woff', binary: true },
32-
{ test: /.woff2$/, type: 'font/woff2', binary: true },
13+
{ test: /.bmp$/, contentType: 'image/bmp', text: false },
14+
{ test: /.png$/, contentType: 'image/png', text: false },
15+
{ test: /.gif$/, contentType: 'image/gif', text: false },
16+
{ test: /.jp(e)?g$/, contentType: 'image/jpeg', text: false },
17+
{ test: /.ico$/, contentType: 'image/vnd.microsoft.icon', text: false },
18+
{ test: /.tif(f)?$/, contentType: 'image/png', text: false },
19+
{ test: /.aac$/, contentType: 'audio/aac', text: false },
20+
{ test: /.mp3$/, contentType: 'audio/mpeg', text: false },
21+
{ test: /.avi$/, contentType: 'video/x-msvideo', text: false },
22+
{ test: /.mp4$/, contentType: 'video/mp4', text: false },
23+
{ test: /.mpeg$/, contentType: 'video/mpeg', text: false },
24+
{ test: /.webm$/, contentType: 'video/webm', text: false },
25+
{ test: /.pdf$/, contentType: 'application/pdf', text: false },
26+
{ test: /.tar$/, contentType: 'application/x-tar', text: false },
27+
{ test: /.zip$/, contentType: 'application/zip', text: false },
28+
{ test: /.eot$/, contentType: 'application/vnd.ms-fontobject', text: false },
29+
{ test: /.otf$/, contentType: 'font/otf', text: false },
30+
{ test: /.ttf$/, contentType: 'font/ttf', text: false },
31+
{ test: /.woff$/, contentType: 'font/woff', text: false },
32+
{ test: /.woff2$/, contentType: 'font/woff2', text: false },
3333
];
3434

3535
function mergeContentTypes(contentTypes) {
@@ -51,23 +51,23 @@ function mergeContentTypes(contentTypes) {
5151
invalid = true;
5252
}
5353

54-
if(typeof contentType.type !== 'string' || contentType.type.indexOf('/') === -1) {
54+
if(typeof contentType.contentType !== 'string' || contentType.contentType.indexOf('/') === -1) {
5555
console.log(`⚠️ Ignoring contentTypes[${index}]: 'type' must be a string representing a MIME type.`);
5656
invalid = true;
5757
}
5858

59-
if('binary' in contentType && typeof contentType.binary !== 'boolean') {
60-
console.log(`⚠️ Ignoring contentTypes[${index}]: optional 'binary' must be a boolean value.`);
59+
if('text' in contentType && typeof contentType.text !== 'boolean') {
60+
console.log(`⚠️ Ignoring contentTypes[${index}]: optional 'text' must be a boolean value.`);
6161
invalid = true;
6262
}
6363

6464
if(!invalid) {
6565
const contentTypeDef = {
6666
test: contentType.test,
67-
type: contentType.type,
67+
contentType: contentType.contentType,
6868
};
69-
if(contentType.binary != null) {
70-
contentTypeDef.binary = contentType.binary;
69+
if(contentType.text != null) {
70+
contentTypeDef.text = contentType.text;
7171
}
7272
finalContentTypes.push(contentTypeDef);
7373
}
@@ -83,17 +83,17 @@ function mergeContentTypes(contentTypes) {
8383
return finalContentTypes;
8484
}
8585

86-
function testFileContentType(contentTypes, file) {
86+
function testFileContentType(contentTypes, assetKey) {
8787
for (const contentType of contentTypes ?? defaultContentTypes) {
8888
let matched = false;
8989
if(contentType.test instanceof RegExp) {
90-
matched = contentType.test.test(file);
90+
matched = contentType.test.test(assetKey);
9191
} else {
9292
// should be a function
93-
matched = contentType.test(file);
93+
matched = contentType.test(assetKey);
9494
}
9595
if(matched) {
96-
return { type: contentType.type, binary: Boolean(contentType.binary) };
96+
return { contentType: contentType.contentType, text: Boolean(contentType.text ?? false) };
9797
}
9898
}
9999
return null;

resources/default-content-types.d.ts

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
export type ContentTypeDef = {
2-
test: RegExp | ((path: string) => boolean),
3-
type: string,
4-
binary?: boolean,
5-
};
1+
// Content Type test
62

7-
export type ContentTypeTestResult = {
8-
type: string,
9-
binary: boolean,
10-
};
3+
import {
4+
ContentTypeDef,
5+
ContentTypeTestResult,
6+
} from '@fastly/compute-js-static-publish';
117

128
declare const defaultContentTypes: ContentTypeDef[];
13-
149
declare function mergeContentTypes(entries?: ContentTypeDef[]): ContentTypeDef[];
15-
declare function testFileContentType(entries: ContentTypeDef[] | null | undefined, path: string): ContentTypeTestResult | null;
10+
declare function testFileContentType(entries: ContentTypeDef[] | null | undefined, assetKey: string): ContentTypeTestResult | null;

resources/index.js

+8-114
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,16 @@
11
/// <reference types="@fastly/js-compute" />
22

3-
import { Router } from '@fastly/expressly';
4-
import { staticAssets, spaFile, notFoundPageFile, autoIndex, autoExt } from './statics';
3+
import { getServer } from './statics.js';
54

6-
const router = new Router();
5+
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
6+
async function handleRequest(event) {
77

8-
function getMatchingRequestPath(path) {
8+
const server = getServer();
9+
const response = await server.serveRequest(event.request);
910

10-
if(!path.endsWith('/')) {
11-
// A path that does not end in a slash can match an asset directly
12-
if (staticAssets.getAsset(path) != null) {
13-
return path;
14-
}
15-
16-
// ... or, we can try auto-ext:
17-
// looks for an asset that has the specified suffix (usually extension, such as .html)
18-
if(Array.isArray(autoExt)) {
19-
for (const extEntry of autoExt) {
20-
let pathWithExt = path + extEntry;
21-
if (staticAssets.getAsset(pathWithExt) != null) {
22-
return pathWithExt;
23-
}
24-
}
25-
}
26-
}
27-
28-
// try auto-index:
29-
// treats the path as a directory, and looks for an asset with the specified
30-
// suffix (usually an index file, such as index.html)
31-
let dir = path;
32-
// remove all slashes from end, and add one trailing slash
33-
while(dir.endsWith('/')) {
34-
dir = dir.slice(0, -1);
11+
if (response != null) {
12+
return response;
3513
}
36-
dir = dir + '/';
37-
if(Array.isArray(autoIndex)) {
38-
for (const indexEntry of autoIndex) {
39-
let indexPath = dir + indexEntry;
40-
if (staticAssets.getAsset(indexPath) != null) {
41-
return indexPath;
42-
}
43-
}
44-
}
45-
46-
return null;
47-
}
4814

49-
function requestAcceptsTextHtml(req) {
50-
const accept = (req.headers.get('Accept') ?? '')
51-
.split(',')
52-
.map(x => x.split(';')[0]);
53-
if(!accept.includes('text/html') && !accept.includes('*/*') && accept.includes('*')) {
54-
return false;
55-
}
56-
return true;
15+
return new Response('Not found', { status: 404 });
5716
}
58-
59-
router.get("*", (req, res) => {
60-
const assetPath = getMatchingRequestPath(req.urlObj.pathname);
61-
const asset = staticAssets.getAsset(assetPath);
62-
if(asset == null) {
63-
return;
64-
}
65-
66-
const response = staticAssets.serveAsset(asset);
67-
res.send(response);
68-
});
69-
70-
// TODO: If you need to handle any API routes, add them here.
71-
// router.get("/api/endpoint", (req, res) => {
72-
// res.send("foo");
73-
// });
74-
75-
// If this is a SPA, then return index.html for HTML requests
76-
router.get("*", (req, res) => {
77-
if(!spaFile) {
78-
return;
79-
}
80-
if(!requestAcceptsTextHtml(req)) {
81-
return;
82-
}
83-
const asset = staticAssets.getAsset(spaFile);
84-
if(asset == null) {
85-
return;
86-
}
87-
88-
const response = new Response(asset.content, {
89-
status: 200,
90-
headers: {
91-
'Cache-Control': 'no-cache',
92-
'Content-Type': 'text/html',
93-
}
94-
});
95-
res.send(response);
96-
});
97-
98-
router.all("*", (req, res) => {
99-
if(notFoundPageFile && requestAcceptsTextHtml(req)) {
100-
const asset = staticAssets.getAsset(notFoundPageFile);
101-
if(asset != null) {
102-
const response = new Response(asset.content, {
103-
status: 404,
104-
headers: {
105-
'Cache-Control': 'no-cache',
106-
'Content-Type': 'text/html',
107-
}
108-
});
109-
res.send(response);
110-
return;
111-
}
112-
}
113-
114-
res.send(new Response("404 Not Found", {
115-
status: 404,
116-
headers: {
117-
'Content-Type': 'text/plain',
118-
},
119-
}));
120-
});
121-
122-
router.listen();

resources/statics-metadata.d.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Generated by @fastly/compute-js-static-publish.
3+
*/
4+
5+
import type {
6+
ContentAssetMetadataMap,
7+
} from "@fastly/compute-js-static-publish";
8+
9+
export declare const objectStoreName: string | null;
10+
export declare const contentAssetMetadataMap: ContentAssetMetadataMap;

resources/statics.d.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22
* Generated by @fastly/compute-js-static-publish.
33
*/
44

5-
import type { AssetsMap, StaticAssets } from "@fastly/compute-js-static-publish";
6-
export declare const assets: AssetsMap;
7-
export declare const spaFile: string | false;
8-
export declare const notFoundPageFile: string | false;
9-
export declare const autoIndex: string[] | false;
10-
export declare const autoExt: string[] | false;
11-
export declare const staticAssets: StaticAssets;
5+
import type {
6+
ModuleAssetMap,
7+
PublisherServerConfigNormalized,
8+
ContentAssets,
9+
ModuleAssets,
10+
PublisherServer,
11+
} from '@fastly/compute-js-static-publish';
12+
13+
export declare const moduleAssetMap: ModuleAssetMap;
14+
15+
export declare const serverConfig: PublisherServerConfigNormalized;
16+
export declare const contentAssets: ContentAssets;
17+
export declare const moduleAssets: ModuleAssets;
18+
19+
export declare function getServer(): PublisherServer;

src/assets/asset-manager.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export abstract class AssetManager<TAsset> {
2+
3+
protected readonly assets: Record<string, TAsset>;
4+
5+
protected constructor() {
6+
this.assets = {};
7+
}
8+
9+
setAsset(assetKey: string, asset: TAsset) {
10+
this.assets[assetKey] = asset;
11+
}
12+
13+
getAsset(assetKey: string): TAsset | null {
14+
return this.assets[assetKey] ?? null;
15+
}
16+
17+
}

0 commit comments

Comments
 (0)