Urara-Blog/node_modules/.pnpm-store/v3/files/f7/d37dfa448050924d90be756d06a49ac98a99b876782c2784982eb0774f2852485d24e3d68bc31bf6cc3351cf26d948740d0266eca2a0299c89b14d30f2901e
2022-08-14 01:14:53 +08:00

320 lines
9.2 KiB
Text

import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
import { dirname, join, resolve, posix } from 'path';
import { fileURLToPath } from 'url';
import glob from 'tiny-glob/sync.js';
import esbuild from 'esbuild';
import toml from '@iarna/toml';
/**
* @typedef {{
* build?: { publish?: string }
* functions?: { node_bundler?: 'zisi' | 'esbuild' }
* } & toml.JsonMap} NetlifyConfig
*/
/**
* @typedef {{
* functions: Array<
* | {
* function: string;
* path: string;
* }
* | {
* function: string;
* pattern: string;
* }
* >;
* version: 1;
* }} HandlerManifest
*/
const files = fileURLToPath(new URL('./files', import.meta.url).href);
const edge_set_in_env_var =
process.env.NETLIFY_SVELTEKIT_USE_EDGE === 'true' ||
process.env.NETLIFY_SVELTEKIT_USE_EDGE === '1';
/** @type {import('.').default} */
export default function ({ split = false, edge = edge_set_in_env_var } = {}) {
return {
name: '@sveltejs/adapter-netlify',
async adapt(builder) {
const netlify_config = get_netlify_config();
// "build" is the default publish directory when Netlify detects SvelteKit
const publish = get_publish_directory(netlify_config, builder) || 'build';
// empty out existing build directories
builder.rimraf('.netlify/edge-functions');
builder.rimraf('.netlify/functions-internal');
builder.rimraf('.netlify/server');
builder.rimraf('.netlify/package.json');
builder.rimraf('.netlify/serverless.js');
builder.log.minor(`Publishing to "${publish}"`);
builder.log.minor('Copying assets...');
builder.writeClient(publish);
builder.writePrerendered(publish);
builder.log.minor('Writing custom headers...');
const headers_file = join(publish, '_headers');
builder.copy('_headers', headers_file);
appendFileSync(
headers_file,
`\n\n/${builder.config.kit.appDir}/immutable/*\n cache-control: public\n cache-control: immutable\n cache-control: max-age=31536000\n`
);
// for esbuild, use ESM
// for zip-it-and-ship-it, use CJS until https://github.com/netlify/zip-it-and-ship-it/issues/750
const esm = netlify_config?.functions?.node_bundler === 'esbuild';
if (edge) {
if (split) {
throw new Error('Cannot use `split: true` alongside `edge: true`');
}
await generate_edge_functions({ builder });
} else {
await generate_lambda_functions({ builder, esm, split, publish });
}
}
};
}
/**
* @param { object } params
* @param {import('@sveltejs/kit').Builder} params.builder
*/
async function generate_edge_functions({ builder }) {
const tmp = builder.getBuildDirectory('netlify-tmp');
builder.rimraf(tmp);
builder.mkdirp(tmp);
builder.mkdirp('.netlify/edge-functions');
// Don't match the static directory
const pattern = '^/.*$';
// Go doesn't support lookarounds, so we can't do this
// const pattern = appDir ? `^/(?!${escapeStringRegexp(appDir)}).*$` : '^/.*$';
/** @type {HandlerManifest} */
const edge_manifest = {
functions: [
{
function: 'render',
pattern
}
],
version: 1
};
builder.log.minor('Generating Edge Function...');
const relativePath = posix.relative(tmp, builder.getServerDirectory());
builder.copy(`${files}/edge.js`, `${tmp}/entry.js`, {
replace: {
'0SERVER': `${relativePath}/index.js`,
MANIFEST: './manifest.js'
}
});
const manifest = builder.generateManifest({
relativePath
});
writeFileSync(
`${tmp}/manifest.js`,
`export const manifest = ${manifest};\n\nexport const prerendered = new Set(${JSON.stringify(
builder.prerendered.paths
)});\n`
);
await esbuild.build({
entryPoints: [`${tmp}/entry.js`],
outfile: '.netlify/edge-functions/render.js',
bundle: true,
format: 'esm',
platform: 'browser',
sourcemap: 'linked',
target: 'es2020'
});
writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest));
}
/**
* @param { object } params
* @param {import('@sveltejs/kit').Builder} params.builder
* @param { string } params.publish
* @param { boolean } params.split
* @param { boolean } params.esm
*/
async function generate_lambda_functions({ builder, publish, split, esm }) {
builder.mkdirp('.netlify/functions-internal');
/** @type {string[]} */
const redirects = [];
builder.writeServer('.netlify/server');
const replace = {
'0SERVER': './server/index.js' // digit prefix prevents CJS build from using this as a variable name, which would also get replaced
};
if (esm) {
builder.copy(`${files}/esm`, '.netlify', { replace });
} else {
glob('**/*.js', { cwd: '.netlify/server' }).forEach((file) => {
const filepath = `.netlify/server/${file}`;
const input = readFileSync(filepath, 'utf8');
const output = esbuild.transformSync(input, { format: 'cjs', target: 'node12' }).code;
writeFileSync(filepath, output);
});
builder.copy(`${files}/cjs`, '.netlify', { replace });
writeFileSync(join('.netlify', 'package.json'), JSON.stringify({ type: 'commonjs' }));
}
if (split) {
builder.log.minor('Generating serverless functions...');
await builder.createEntries((route) => {
const parts = [];
// Netlify's syntax uses '*' and ':param' as "splats" and "placeholders"
// https://docs.netlify.com/routing/redirects/redirect-options/#splats
for (const segment of route.segments) {
if (segment.rest) {
parts.push('*');
break; // Netlify redirects don't allow anything after a *
} else if (segment.dynamic) {
parts.push(`:${parts.length}`);
} else {
parts.push(segment.content);
}
}
const pattern = `/${parts.join('/')}`;
const name = parts.join('-').replace(/[:.]/g, '_').replace('*', '__rest') || 'index';
return {
id: pattern,
filter: (other) => matches(route.segments, other.segments),
complete: (entry) => {
const manifest = entry.generateManifest({
relativePath: '../server',
format: esm ? 'esm' : 'cjs'
});
const fn = esm
? `import { init } from '../serverless.js';\n\nexport const handler = init(${manifest});\n`
: `const { init } = require('../serverless.js');\n\nexports.handler = init(${manifest});\n`;
writeFileSync(`.netlify/functions-internal/${name}.js`, fn);
redirects.push(`${pattern} /.netlify/functions/${name} 200`);
redirects.push(`${pattern}/__data.json /.netlify/functions/${name} 200`);
}
};
});
} else {
builder.log.minor('Generating serverless functions...');
const manifest = builder.generateManifest({
relativePath: '../server',
format: esm ? 'esm' : 'cjs'
});
const fn = esm
? `import { init } from '../serverless.js';\n\nexport const handler = init(${manifest});\n`
: `const { init } = require('../serverless.js');\n\nexports.handler = init(${manifest});\n`;
writeFileSync('.netlify/functions-internal/render.js', fn);
redirects.push('* /.netlify/functions/render 200');
}
// this should happen at the end, after builder.writeClient(...),
// so that generated redirects are appended to custom redirects
// rather than replaced by them
builder.log.minor('Writing redirects...');
const redirect_file = join(publish, '_redirects');
if (existsSync('_redirects')) {
builder.copy('_redirects', redirect_file);
}
builder.mkdirp(dirname(redirect_file));
appendFileSync(redirect_file, `\n\n${redirects.join('\n')}`);
}
function get_netlify_config() {
if (!existsSync('netlify.toml')) return null;
try {
return /** @type {NetlifyConfig} */ (toml.parse(readFileSync('netlify.toml', 'utf-8')));
} catch (err) {
err.message = `Error parsing netlify.toml: ${err.message}`;
throw err;
}
}
/**
* @param {NetlifyConfig} netlify_config
* @param {import('@sveltejs/kit').Builder} builder
**/
function get_publish_directory(netlify_config, builder) {
if (netlify_config) {
if (!netlify_config.build?.publish) {
builder.log.minor('No publish directory specified in netlify.toml, using default');
return;
}
if (netlify_config.redirects) {
throw new Error(
"Redirects are not supported in netlify.toml. Use _redirects instead. For more details consult the readme's troubleshooting section."
);
}
if (resolve(netlify_config.build.publish) === process.cwd()) {
throw new Error(
'The publish directory cannot be set to the site root. Please change it to another value such as "build" in netlify.toml.'
);
}
return netlify_config.build.publish;
}
builder.log.warn(
'No netlify.toml found. Using default publish directory. Consult https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify#configuration for more details '
);
}
/**
* @typedef {{ rest: boolean, dynamic: boolean, content: string }} RouteSegment
*/
/**
* @param {RouteSegment[]} a
* @param {RouteSegment[]} b
* @returns {boolean}
*/
function matches(a, b) {
if (a[0] && b[0]) {
if (b[0].rest) {
if (b.length === 1) return true;
const next_b = b.slice(1);
for (let i = 0; i < a.length; i += 1) {
if (matches(a.slice(i), next_b)) return true;
}
return false;
}
if (!b[0].dynamic) {
if (!a[0].dynamic && a[0].content !== b[0].content) return false;
}
if (a.length === 1 && b.length === 1) return true;
return matches(a.slice(1), b.slice(1));
} else if (a[0]) {
return a.length === 1 && a[0].rest;
} else {
return b.length === 1 && b[0].rest;
}
}