Skip to content

Commit

Permalink
hack, interfaces, move files around
Browse files Browse the repository at this point in the history
  • Loading branch information
lilnasy committed Nov 22, 2023
1 parent 09bcfeb commit 37cb8d3
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 53 deletions.
16 changes: 9 additions & 7 deletions packages/netlify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,21 @@
"bugs": "https://github.com/withastro/adapters/issues",
"homepage": "https://docs.astro.build/en/guides/integrations-guide/netlify/",
"exports": {
".": "./dist/index.js",
"./functions": "./dist/integration-functions.js",
"./server-v1.js": "./dist/server-v1.js",
"./server-v2.js": "./dist/server-v2.js",
".": "./src/index.ts",
"./functions": "./src/integration-lambda.ts",
"./builders": "./src/integration-builders.ts",
"./v2": "./src/integration-v2.ts",
"./server-lambda": "./src/server-lambda.ts",
"./server-v2": "./src/server-v2.ts",
"./builder-types": "./builder-types.d.ts",
"./package.json": "./package.json"
},
"files": [
"dist"
"builders-types.d.ts",
"src"
],
"scripts": {
"build": "tsc",
"test-fn": "mocha --exit --timeout 20000 --file \"./test/setup.js\" test/functions/",
"test-edge": "deno test --allow-run --allow-read --allow-net --allow-env --allow-write ./test/edge-functions/",
"test": "pnpm test-fn",
"test:hosted": "mocha --exit --timeout 30000 test/hosted"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/netlify/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { netlifyFunctions as default, netlifyFunctions } from './integration-functions.js';
export { default, default as netlifyFunctions } from './integration-lambda.ts';
export { netlifyStatic } from './integration-static.js';
82 changes: 51 additions & 31 deletions packages/netlify/src/integration-base.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import type { AstroAdapter, AstroConfig, AstroIntegration, RouteData } from 'astro';
import { writeFile } from 'node:fs/promises';
import { extname, join } from 'node:path';
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { generateEdgeMiddleware } from './middleware.js';
import { createRedirects, type Args } from './shared.js';
import { generateEdgeMiddleware } from './middleware.ts';
import { createRedirects, type Options } from './shared.ts';

export const NETLIFY_EDGE_MIDDLEWARE_FILE = 'netlify-edge-middleware';

export function getAdapter(args: Args): AstroAdapter {
export function getAdapter(options: Required<Options> & InternalOptions): AstroAdapter {
return {
name: '@astrojs/netlify/functions',
serverEntrypoint: args.runtime === 'v2' ? '@astrojs/netlify/server-v2.js' : '@astrojs/netlify/server-v1.js',
exports: args.runtime === 'v2' ? ['default'] : ['handler'],
args,
name: options.adapterName,
serverEntrypoint: options.functionType === 'v2' ? '@astrojs/netlify/server-v2' : '@astrojs/netlify/server-lambda',
exports: options.functionType === 'v2' ? ['default'] : ['handler'],
args: options,
adapterFeatures: {
functionPerRoute: args.functionPerRoute,
edgeMiddleware: args.edgeMiddleware,
functionPerRoute: options.functionPerRoute,
edgeMiddleware: options.edgeMiddleware,
},
supportedAstroFeatures: {
hybridOutput: 'stable',
Expand All @@ -30,37 +31,37 @@ export function getAdapter(args: Args): AstroAdapter {
};
}

interface NetlifyFunctionsOptions {
dist?: URL;
builders?: boolean;
binaryMediaTypes?: string[];
edgeMiddleware?: boolean;
functionPerRoute?: boolean;
runtime?: 'v1' | 'v2';
interface InternalOptions {
adapterName: string;
functionType: 'lambda-compatible' | 'builders' | 'v2';
dist?: URL
}

function netlifyFunctions({
class StacklessError extends Error { trace = undefined }

export function getIntegration({
dist,
binaryMediaTypes,
adapterName,
binaryMediaTypes = [],
builders = false,
functionPerRoute = false,
edgeMiddleware = false,
runtime = 'v1'
}: NetlifyFunctionsOptions = {}): AstroIntegration {
functionType
}: Options & InternalOptions): AstroIntegration {

if (runtime === 'v2' && builders) {
throw new Error("Builder functions are not compatible with Netlify's Runtime V2. Please either disable builders or switch back to V1.")
if (functionType === 'v2' && builders) {
throw new StacklessError("Builder functions are not compatible with Netlify Functions 2.0. Please either disable builders or switch back to lambda compatible function.")
}

let _config: AstroConfig;
let _entryPoints: Map<RouteData, URL>;
let ssrEntryFile: string;
let _middlewareEntryPoint: URL;
let _middlewareEntryPoint: URL | undefined;
return {
name: '@astrojs/netlify',
hooks: {
'astro:config:setup' ({ config, updateConfig }) {
const outDir = dist ?? new URL('./dist/', config.root);
const outDir = dist ?? config.outDir;
updateConfig({
outDir,
build: {
Expand All @@ -79,11 +80,12 @@ function netlifyFunctions({
'astro:config:done' ({ config, setAdapter, logger }) {
setAdapter(
getAdapter({
adapterName,
binaryMediaTypes,
builders,
functionPerRoute,
edgeMiddleware,
runtime
functionType
})
);
_config = config;
Expand All @@ -104,26 +106,46 @@ function netlifyFunctions({
const functionsConfigPath = join(fileURLToPath(_config.build.server), 'entry.json');
await writeFile(functionsConfigPath, JSON.stringify(functionsConfig));

const type = builders ? 'builders' : 'functions';
const kind = type ?? 'functions';
const type = functionType === 'builders' ? 'builders' : 'functions';

if (_entryPoints.size) {
const routeToDynamicTargetMap = new Map();
for (const [route, entryFile] of _entryPoints) {
const wholeFileUrl = fileURLToPath(entryFile);

// HACK: transform entry file manually so that netlify-cli can automatically detect there's a default export
const orginalEntrypointContents = fs.readFileSync(wholeFileUrl, 'utf-8');

const replacedEntrypointContents =
orginalEntrypointContents
.replace("export { _default as default, pageModule };", "export { pageModule };")
.replace("const _default = _exports['default'];", "export default _exports['default'];");

fs.writeFileSync(wholeFileUrl, replacedEntrypointContents);

const extension = extname(wholeFileUrl);
const relative = wholeFileUrl
.replace(fileURLToPath(_config.build.server), '')
.replace(extension, '')
.replaceAll('\\', '/');
const dynamicTarget = `/.netlify/${kind}/${relative}`;
const dynamicTarget = `/.netlify/${type}/${relative}`;

routeToDynamicTargetMap.set(route, dynamicTarget);
}
await createRedirects(_config, routeToDynamicTargetMap, dir);
} else {
const dynamicTarget = `/.netlify/${kind}/${ssrEntryFile}`;
// HACK: transform entry file manually so that netlify-cli can automatically detect there's a default export
const filePath = fileURLToPath(new URL(`./.netlify/functions-internal/${_config.build.serverEntry}`, _config.root))
const originalEntrypointContents = fs.readFileSync(filePath, 'utf-8')

const replacedEntrypointContents =
originalEntrypointContents
.replace("export { _default as default, pageMap };", "export { pageMap };")
.replace("const _default = _exports['default'];", "export default _exports['default'];");

fs.writeFileSync(filePath, replacedEntrypointContents);

const dynamicTarget = `/.netlify/${type}/${ssrEntryFile}`;
const map: [RouteData, string][] = routes.map((route) => {
return [route, dynamicTarget];
});
Expand All @@ -147,5 +169,3 @@ function netlifyFunctions({
},
};
}

export { netlifyFunctions as default, netlifyFunctions };
10 changes: 10 additions & 0 deletions packages/netlify/src/integration-builders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getIntegration } from "./integration-base.ts";
import type { Options } from "./shared.ts";

export default function (options: Options) {
return getIntegration({
...options,
adapterName: "@astrojs/netlify/builders",
functionType: "builders"
});
}
10 changes: 10 additions & 0 deletions packages/netlify/src/integration-lambda.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getIntegration } from "./integration-base.ts";
import type { Options } from "./shared.ts";

export default function (options: Options) {
return getIntegration({
...options,
adapterName: "@astrojs/netlify/lambda",
functionType: "lambda-compatible"
});
}
6 changes: 4 additions & 2 deletions packages/netlify/src/integration-static.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AstroIntegration, RouteData } from 'astro';
import { createRedirects } from './shared.js';
import { createRedirects } from './shared.ts';

export function netlifyStatic(): AstroIntegration {
export function getIntegration(): AstroIntegration {
let _config: any;
return {
name: '@astrojs/netlify',
Expand All @@ -28,3 +28,5 @@ export function netlifyStatic(): AstroIntegration {
},
};
}

export { getIntegration as default, getIntegration as netlifyStatic };
10 changes: 10 additions & 0 deletions packages/netlify/src/integration-v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getIntegration } from "./integration-base.ts";
import type { Options } from "./shared.ts";

export default function (options: Omit<Options, "builders">) {
return getIntegration({
...options,
adapterName: "@astrojs/netlify/v2",
functionType: "v2"
});
}
2 changes: 1 addition & 1 deletion packages/netlify/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { ASTRO_LOCALS_HEADER } from './shared.js';
import { ASTRO_LOCALS_HEADER } from './shared.ts';

const DENO_SHIM = `globalThis.process = {
argv: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { builder, type Handler } from '@netlify/functions';
import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
import { applyPolyfills } from 'astro/app/node';
import { clientAddressSymbol, ASTRO_LOCALS_HEADER, type Args } from './shared.js';
import { clientAddressSymbol, ASTRO_LOCALS_HEADER, type Options } from './shared.ts';

applyPolyfills();

function parseContentType(header?: string) {
return header?.split(';')[0] ?? '';
}

export const createExports = (manifest: SSRManifest, { builders, binaryMediaTypes = [] }: Args) => {
export const createExports = (manifest: SSRManifest, { builders, binaryMediaTypes = [] }: Options) => {
const app = new App(manifest);

const knownBinaryMediaTypes = new Set([
Expand Down
4 changes: 2 additions & 2 deletions packages/netlify/src/server-v2.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { App } from 'astro/app';
import { applyPolyfills } from 'astro/app/node';
import { clientAddressSymbol, ASTRO_LOCALS_HEADER, type Args } from './shared.js';
import { clientAddressSymbol, ASTRO_LOCALS_HEADER, type Options } from './shared.ts';
import type { Context } from '@netlify/functions';
import type { SSRManifest } from 'astro';

applyPolyfills();

export function createExports (manifest: SSRManifest, _args: Args) {
export function createExports (manifest: SSRManifest, _options: Options) {
const app = new App(manifest);

return {
Expand Down
12 changes: 7 additions & 5 deletions packages/netlify/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { createRedirectsFromAstroRoutes } from '@astrojs/underscore-redirects';
import fs from 'node:fs';
import type { AstroConfig, RouteData } from 'astro';

export interface Args {
builders: boolean;
export interface Options {
/**
* @deprecated Import from "@astrojs/netlify/builders" instead.
*/
builders?: boolean;
binaryMediaTypes?: string[];
edgeMiddleware: boolean;
functionPerRoute: boolean;
runtime: 'v1' | 'v2';
edgeMiddleware?: boolean;
functionPerRoute?: boolean;
}

export const clientAddressSymbol = Symbol.for('astro.clientAddress');
Expand Down
1 change: 0 additions & 1 deletion packages/netlify/test/functions/split-support.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ describe('Split support', () => {
root: new URL('./fixtures/split-support/', import.meta.url).toString(),
output: 'server',
adapter: netlifyAdapter({
dist: new URL('./fixtures/split-support/dist/', import.meta.url),
functionPerRoute: true,
}),
site: `http://example.com`,
Expand Down
2 changes: 1 addition & 1 deletion packages/netlify/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"extends": "astro/tsconfigs/strict",
"include": ["src"],
"compilerOptions": {
"outDir": "./dist",
Expand Down

0 comments on commit 37cb8d3

Please sign in to comment.