0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-02-03 22:29:08 -05:00

feat: (naively) minify astro-generated JS and CSS

This commit is contained in:
Nate Moore 2022-03-02 11:05:06 -06:00
parent b3b18554df
commit 4fc4bea975
3 changed files with 38 additions and 10 deletions

View file

@ -1,6 +1,6 @@
import type { AstroComponentMetadata } from '../../@types/astro';
import type { SSRElement, SSRResult } from '../../@types/astro';
import { hydrationSpecifier, serializeListValue } from './util.js';
import { hydrationSpecifier, serializeListValue, naiveMinify } from './util.js';
import serializeJavaScript from 'serialize-javascript';
// Serializes props passed into a component so that they can be reused during hydration.
@ -91,7 +91,8 @@ interface HydrateScriptOptions {
export async function generateHydrateScript(scriptOptions: HydrateScriptOptions, metadata: Required<AstroComponentMetadata>): Promise<SSRElement> {
const { renderer, result, astroId, props } = scriptOptions;
const { hydrate, componentUrl, componentExport } = metadata;
const PROPS_PLACEHOLDER = `/*astro:PROPS*/`;
const HYDRATE_ARGS_PLACEHOLDER = `/*astro:HYDRATE_ARGS*/`;
if (!componentExport) {
throw new Error(`Unable to resolve a componentExport for "${metadata.displayName}"! Please open an issue.`);
}
@ -107,19 +108,32 @@ export async function generateHydrateScript(scriptOptions: HydrateScriptOptions,
? `const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${await result.resolve(componentUrl)}"), import("${await result.resolve(
renderer.source
)}")]);
return (el, children) => hydrate(el)(Component, ${serializeProps(props)}, children);
return (el, children) => hydrate(el)(Component, ${PROPS_PLACEHOLDER}, children);
`
: `await import("${await result.resolve(componentUrl)}");
return () => {};
`;
hydrationSource = `
import setup from '${await result.resolve(hydrationSpecifier(hydrate))}';
setup("${astroId}", {name:"${metadata.displayName}",${metadata.hydrateArgs ? `value:${HYDRATE_ARGS_PLACEHOLDER}` : ''}}, async () => {
${hydrationSource}
});
`;
// Minify output (we know all these identifiers)
// Important! Use placeholder comments to avoid
// minifying user content inside of strings
hydrationSource = naiveMinify(hydrationSource, {
setup: 'a',
hydrate: 'b',
Component: 'c',
children: 'd',
el: 'e',
})
.replace(PROPS_PLACEHOLDER, serializeProps(props))
.replace(HYDRATE_ARGS_PLACEHOLDER, JSON.stringify(metadata.hydrateArgs));
const hydrationScript = {
props: { type: 'module', 'data-astro-component-hydration': true },
children: `import setup from '${await result.resolve(hydrationSpecifier(hydrate))}';
setup("${astroId}", {name:"${metadata.displayName}",${metadata.hydrateArgs ? `value: ${JSON.stringify(metadata.hydrateArgs)}` : ''}}, async () => {
${hydrationSource}
});
`,
children: hydrationSource,
};
return hydrationScript;

View file

@ -460,7 +460,7 @@ export async function renderPage(result: SSRResult, Component: AstroComponentFac
});
});
if (needsHydrationStyles) {
styles.push(renderElement('style', { props: { 'astro-style': true }, children: 'astro-root, astro-fragment { display: contents; }' }));
styles.push(renderElement('style', { props: { 'astro-style': true }, children: 'astro-root,astro-fragment{display:contents;}' }));
}
const links = Array.from(result.links)

View file

@ -43,3 +43,17 @@ export function serializeListValue(value: any) {
export function hydrationSpecifier(hydrate: string) {
return `astro/client/${hydrate}.js`;
}
/**
* Minifies JS without parsing, just replacing whitespace and passed identifiers.
* @param source a string of JS
* @param identifiers any specific identifiers that should be replaced
* @returns a minified source string
*/
export function naiveMinify(source: string, identifiers: Record<string, string> = {}): string {
source = source.trim().replace(/\s+/g, ' ').replace(/([^a-z])\s+/g, '$1').replace(/\s+([^a-zA-Z])/g, '$1');
for (const [identifier, char] of Object.entries(identifiers)) {
source = source.replaceAll(identifier, char);
}
return source;
}