Skip to content

Commit

Permalink
feat: inline stage 2 ESZIP bundler (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardoboucas committed Aug 22, 2022
1 parent 7d12ec2 commit 5df5291
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 1 deletion.
2 changes: 1 addition & 1 deletion deno/bundle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { writeStage2 } from 'https://62f5f45fbc76ed0009624267--edge.netlify.com/bundler/mod.ts'
import { writeStage2 } from './lib/stage2.ts'

const [payload] = Deno.args
const { basePath, destPath, functions, imports } = JSON.parse(payload)
Expand Down
47 changes: 47 additions & 0 deletions deno/lib/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { load } from "https://deno.land/x/eszip@v0.18.0/loader.ts";
import { LoadResponse } from "https://deno.land/x/eszip@v0.18.0/mod.ts";
import * as path from "https://deno.land/std@0.127.0/path/mod.ts";
import { retryAsync } from "https://deno.land/x/retry@v2.0.0/mod.ts";

const inlineModule = (
specifier: string,
content: string,
): LoadResponse => {
return {
content,
headers: {
"content-type": "application/typescript",
},
kind: "module",
specifier,
};
};

const loadFromVirtualRoot = async (
specifier: string,
virtualRoot: string,
basePath: string,
) => {
const basePathURL = path.toFileUrl(basePath).toString();
const filePath = specifier.replace(virtualRoot.slice(0, -1), basePathURL);
const file = await load(filePath);

if (file === undefined) {
throw new Error(`Could not find file: ${filePath}`);
}

return { ...file, specifier };
};

const loadWithRetry = (specifier: string, delay = 1000, maxTry = 3) => {
if (!specifier.startsWith("https://")) {
return load(specifier);
}

return retryAsync(() => load(specifier), {
delay,
maxTry,
});
};

export { inlineModule, loadFromVirtualRoot, loadWithRetry };
4 changes: 4 additions & 0 deletions deno/lib/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const PUBLIC_SPECIFIER = "netlify:edge";
export const STAGE1_SPECIFIER = "netlify:bootstrap-stage1";
export const STAGE2_SPECIFIER = "netlify:bootstrap-stage2";
export const virtualRoot = "file:///root/";
81 changes: 81 additions & 0 deletions deno/lib/stage2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { build, LoadResponse } from 'https://deno.land/x/eszip@v0.18.0/mod.ts'

import * as path from 'https://deno.land/std@0.127.0/path/mod.ts'

import { PUBLIC_SPECIFIER, STAGE2_SPECIFIER, virtualRoot } from './consts.ts'
import { inlineModule, loadFromVirtualRoot, loadWithRetry } from './common.ts'

interface InputFunction {
name: string
path: string
}

interface WriteStage2Options {
basePath: string
destPath: string
functions: InputFunction[]
imports?: Record<string, string>
}

const getFunctionReference = (basePath: string, func: InputFunction, index: number) => {
const importName = `func${index}`
const exportLine = `"${func.name}": ${importName}`
const url = getVirtualPath(basePath, func.path)

return {
exportLine,
importLine: `import ${importName} from "${url}";`,
}
}

const getStage2Entry = (basePath: string, functions: InputFunction[]) => {
const lines = functions.map((func, index) => getFunctionReference(basePath, func, index))
const importLines = lines.map(({ importLine }) => importLine).join('\n')
const exportLines = lines.map(({ exportLine }) => exportLine).join(', ')
const exportDeclaration = `export const functions = {${exportLines}};`

return [importLines, exportDeclaration].join('\n\n')
}

const getVirtualPath = (basePath: string, filePath: string) => {
const relativePath = path.relative(basePath, filePath)
const url = new URL(relativePath, virtualRoot)

return url
}

const stage2Loader = (basePath: string, functions: InputFunction[], imports: Record<string, string> = {}) => {
return async (specifier: string): Promise<LoadResponse | undefined> => {
if (specifier === STAGE2_SPECIFIER) {
const stage2Entry = getStage2Entry(basePath, functions)

return inlineModule(specifier, stage2Entry)
}

if (specifier === PUBLIC_SPECIFIER) {
return {
kind: 'external',
specifier,
}
}

if (imports[specifier] !== undefined) {
return await loadWithRetry(imports[specifier])
}

if (specifier.startsWith(virtualRoot)) {
return loadFromVirtualRoot(specifier, virtualRoot, basePath)
}

return await loadWithRetry(specifier)
}
}

const writeStage2 = async ({ basePath, destPath, functions, imports }: WriteStage2Options) => {
const loader = stage2Loader(basePath, functions, imports)
const bytes = await build([STAGE2_SPECIFIER], loader)

return await Deno.writeFile(destPath, bytes)
}

export { writeStage2 }

0 comments on commit 5df5291

Please sign in to comment.