Compare commits

...
Sign in to create a new pull request.

5 commits

Author SHA1 Message Date
Nate Moore
4fc4bea975 feat: (naively) minify astro-generated JS and CSS 2022-03-02 11:05:06 -06:00
Nate Moore
b3b18554df feat: add buildOptions.minifyHTML option 2022-03-02 11:04:39 -06:00
Matthew Phillips
e9630b86b3 Migrate more tests to the static build (#2693)
* fix: disable HMR during build (#2684)

* Migrate more tests to the static build

* Only prepend links in non-legacy mode

* Add the 0-css tests

* Convert all CSS tests to the static build

* Migrate Astro global tests

* Remove .only

* Fix static build tests

* Migrate a few more

* More tests

* Move the lit test back to legacy

* Increase the test timeout

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
2022-03-02 10:03:13 -06:00
Matthew Phillips
07b19b0e46 Move astro-basic test to use static build (#2682)
* Move some tests over to the static build (#2677)

* Move some tests over to the static build

* Fix assets tests

* Fix the assets tests

* Fix for the client:only components

* Moves asset tests to the static build

* Move postcss test over to static build

* Bring back legacy build for astro-basic test

* Move astro-basic test to use static build
2022-03-02 10:03:12 -06:00
Matthew Phillips
a4e2b0d580 Unflag the static build (#2652)
* Unflag the static build

* Only set legacyBuild to false if experimentalSSR is true

* Use legacy build when we have to

* Put a few more tests into legacy mode

* Last two

* Make astro-basic use the legacy build

* Adds a changeset

* Mark the lit test as legacy

* Update yarn lock

* Update based on feedback

* Add --legacy-build flag
2022-03-02 10:03:12 -06:00
80 changed files with 433 additions and 651 deletions

View file

@ -0,0 +1,19 @@
---
'astro': minor
---
New default build strategy
This change marks the "static build" as the new default build strategy. If you are unfamiliar with this build strategy check out the [migration guide](https://docs.astro.build/en/migrate/#planned-deprecations) on how to change your code to be compatible with this new bulid strategy.
If you'd like to keep using the old build strategy, use the flag `--legacy-build` both in your `astro dev` and `astro build` scripts, for ex:
```json
{
"scripts": {
"build": "astro build --legacy-build"
}
}
```
Note that the legacy build *is* deprecated and will be removed in a future version. You should only use this flag until you have the time to migration your code.

View file

@ -52,11 +52,11 @@
"dev": "astro-scripts dev \"src/**/*.ts\"",
"postbuild": "astro-scripts copy \"src/**/*.astro\"",
"benchmark": "node test/benchmark/dev.bench.js && node test/benchmark/build.bench.js",
"test": "mocha --parallel --timeout 15000 --ignore **/lit-element.test.js && mocha **/lit-element.test.js",
"test:match": "mocha --timeout 15000 -g"
"test": "mocha --parallel --timeout 20000 --ignore **/lit-element.test.js && mocha --timeout 20000 **/lit-element.test.js",
"test:match": "mocha --timeout 20000 -g"
},
"dependencies": {
"@astrojs/compiler": "^0.11.4",
"@astrojs/compiler": "^0.12.0-next.5",
"@astrojs/language-server": "^0.8.6",
"@astrojs/markdown-remark": "^0.6.2",
"@astrojs/prism": "0.4.0",
@ -82,6 +82,7 @@
"fast-glob": "^3.2.7",
"fast-xml-parser": "^4.0.0-beta.3",
"html-entities": "^2.3.2",
"html-minifier-terser": "^7.0.0-alpha.1",
"htmlparser2": "^7.1.2",
"kleur": "^4.1.4",
"magic-string": "^0.25.7",
@ -116,6 +117,7 @@
"@types/chai": "^4.2.22",
"@types/common-ancestor-path": "^1.0.0",
"@types/connect": "^3.4.35",
"@types/html-minifier-terser": "^6.1.0",
"@types/mime": "^2.0.3",
"@types/mocha": "^9.0.0",
"@types/node-fetch": "^3.0.0",

View file

@ -27,8 +27,10 @@ export interface CLIFlags {
hostname?: string;
port?: number;
config?: string;
/** @deprecated */
experimentalStaticBuild?: boolean;
experimentalSsr?: boolean;
legacyBuild?: boolean;
drafts?: boolean;
}
@ -129,12 +131,19 @@ export interface AstroUserConfig {
*/
drafts?: boolean;
/**
* Experimental: Enables "static build mode" for faster builds.
* Enables "legacy build mode" for compatibility with older Astro versions.
* Default: false
*/
legacyBuild?: boolean;
/**
* @deprecated
* Experimental: Enables "static build mode" for faster builds.
* Default: true
*/
experimentalStaticBuild?: boolean;
/**
* Enable a build for SSR support.
* Default: false
*/
experimentalSsr?: boolean;
};
@ -422,7 +431,7 @@ export interface SSRElement {
export interface SSRMetadata {
renderers: Renderer[];
pathname: string;
experimentalStaticBuild: boolean;
legacyBuild: boolean;
}
export interface SSRResult {

View file

@ -46,7 +46,7 @@ export class App {
const scripts = createModuleScriptElementWithSrcSet(info.scripts, manifest.site);
return render({
experimentalStaticBuild: true,
legacyBuild: false,
links,
logging: defaultLogOptions,
markdownRender: manifest.markdown.render,

View file

@ -110,7 +110,7 @@ class AstroBuilder {
timer.buildStart = performance.now();
// Use the new faster static based build.
if (this.config.buildOptions.experimentalStaticBuild) {
if (!this.config.buildOptions.legacyBuild) {
await staticBuild({
allPages,
astroConfig: this.config,

View file

@ -38,8 +38,7 @@ export interface StaticBuildOptions {
const MAX_CONCURRENT_RENDERS = 10;
function addPageName(pathname: string, opts: StaticBuildOptions): void {
const pathrepl = opts.astroConfig.buildOptions.pageUrlFormat === 'directory' ? '/index.html' : pathname === '/' ? 'index.html' : '.html';
opts.pageNames.push(pathname.replace(/\/?$/, pathrepl).replace(/^\//, ''));
opts.pageNames.push(pathname.replace(/\/?$/, '/').replace(/^\//, ''));
}
// Gives back a facadeId that is relative to the root.
@ -130,6 +129,8 @@ export async function staticBuild(opts: StaticBuildOptions) {
const topLevelImports = new Set([
// Any component that gets hydrated
...metadata.hydratedComponentPaths(),
// Client-only components
...metadata.clientOnlyComponentPaths(),
// Any hydration directive like astro/client/idle.js
...metadata.hydrationDirectiveSpecifiers(),
// The client path for each renderer
@ -181,6 +182,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
logLevel: 'error',
mode: 'production',
build: {
...viteConfig.build,
emptyOutDir: false,
manifest: ssr,
minify: false,
@ -338,6 +340,15 @@ async function generatePage(output: OutputChunk, opts: StaticBuildOptions, inter
}
}
async function minifyHTML(html: string) {
const minifier = await import('html-minifier-terser');
return minifier.minify(html, {
removeComments: false,
collapseWhitespace: true,
caseSensitive: true,
});
}
interface GeneratePathOptions {
pageData: PageBuildData;
internals: BuildInternals;
@ -359,12 +370,12 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
debug('build', `Generating: ${pathname}`);
const site = astroConfig.buildOptions.site;
const links = createLinkStylesheetElementSet(linkIds, site);
const links = createLinkStylesheetElementSet(linkIds.reverse(), site);
const scripts = createModuleScriptElementWithSrcSet(hoistedId ? [hoistedId] : [], site);
try {
const html = await render({
experimentalStaticBuild: true,
let html: string | Buffer = await render({
legacyBuild: false,
links,
logging,
markdownRender: astroConfig.markdownOptions.render,
@ -387,6 +398,10 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
site: astroConfig.buildOptions.site,
});
if (opts.astroConfig.buildOptions.minifyHTML) {
html = await minifyHTML(html)
}
const outFolder = getOutFolder(astroConfig, pathname, pageData.route.type);
const outFile = getOutFile(astroConfig, outFolder, pathname, pageData.route.type);
await fs.promises.mkdir(outFolder, { recursive: true });
@ -453,8 +468,7 @@ async function generateManifest(result: RollupOutput, opts: StaticBuildOptions,
}
function getOutRoot(astroConfig: AstroConfig): URL {
const rootPathname = appendForwardSlash(astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/');
return new URL('.' + rootPathname, astroConfig.dist);
return new URL('./', astroConfig.dist);
}
function getServerRoot(astroConfig: AstroConfig): URL {
@ -480,8 +494,10 @@ function getOutFolder(astroConfig: AstroConfig, pathname: string, routeType: Rou
switch (astroConfig.buildOptions.pageUrlFormat) {
case 'directory':
return new URL('.' + appendForwardSlash(pathname), outRoot);
case 'file':
case 'file': {
return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot);
}
}
}
}
@ -494,8 +510,10 @@ function getOutFile(astroConfig: AstroConfig, outFolder: URL, pathname: string,
switch (astroConfig.buildOptions.pageUrlFormat) {
case 'directory':
return new URL('./index.html', outFolder);
case 'file':
return new URL('./' + npath.basename(pathname) + '.html', outFolder);
case 'file': {
const baseName = npath.basename(pathname);
return new URL('./' + (baseName || 'index') + '.html', outFolder);
}
}
}
}

View file

@ -62,7 +62,9 @@ export const AstroConfigSchema = z.object({
.union([z.literal('file'), z.literal('directory')])
.optional()
.default('directory'),
experimentalStaticBuild: z.boolean().optional().default(false),
legacyBuild: z.boolean().optional().default(false),
minifyHTML: z.boolean().optional().default(false),
experimentalStaticBuild: z.boolean().optional().default(true),
experimentalSsr: z.boolean().optional().default(false),
drafts: z.boolean().optional().default(false),
})
@ -130,7 +132,8 @@ function resolveFlags(flags: Partial<Flags>): CLIFlags {
port: typeof flags.port === 'number' ? flags.port : undefined,
config: typeof flags.config === 'string' ? flags.config : undefined,
hostname: typeof flags.hostname === 'string' ? flags.hostname : undefined,
experimentalStaticBuild: typeof flags.experimentalStaticBuild === 'boolean' ? flags.experimentalStaticBuild : false,
legacyBuild: typeof flags.legacyBuild === 'boolean' ? flags.legacyBuild : false,
experimentalStaticBuild: typeof flags.experimentalStaticBuild === 'boolean' ? flags.experimentalStaticBuild : true,
experimentalSsr: typeof flags.experimentalSsr === 'boolean' ? flags.experimentalSsr : false,
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : false,
};
@ -149,6 +152,7 @@ function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags) {
astroConfig.buildOptions.experimentalSsr = flags.experimentalSsr;
if (flags.experimentalSsr) {
astroConfig.buildOptions.experimentalStaticBuild = true;
astroConfig.buildOptions.legacyBuild = false;
}
}
if (typeof flags.drafts === 'boolean') astroConfig.buildOptions.drafts = flags.drafts;

View file

@ -47,7 +47,7 @@ async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise<[Param
}
interface RenderOptions {
experimentalStaticBuild: boolean;
legacyBuild: boolean;
logging: LogOptions;
links: Set<SSRElement>;
markdownRender: MarkdownRenderOptions;
@ -63,7 +63,7 @@ interface RenderOptions {
}
export async function render(opts: RenderOptions): Promise<string> {
const { experimentalStaticBuild, links, logging, origin, markdownRender, mod, pathname, scripts, renderers, resolve, route, routeCache, site } = opts;
const { legacyBuild, links, logging, origin, markdownRender, mod, pathname, scripts, renderers, resolve, route, routeCache, site } = opts;
const [params, pageProps] = await getParamsAndProps({
logging,
@ -84,7 +84,7 @@ export async function render(opts: RenderOptions): Promise<string> {
if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
const result = createResult({
experimentalStaticBuild,
legacyBuild,
links,
logging,
markdownRender,
@ -100,7 +100,7 @@ export async function render(opts: RenderOptions): Promise<string> {
let html = await renderPage(result, Component, pageProps, null);
// inject <!doctype html> if missing (TODO: is a more robust check needed for comments, etc.?)
if (experimentalStaticBuild && !/<!doctype html/i.test(html)) {
if (!legacyBuild && !/<!doctype html/i.test(html)) {
html = '<!DOCTYPE html>\n' + html;
}

View file

@ -1,5 +1,5 @@
import type * as vite from 'vite';
import type { AstroConfig, ComponentInstance, Renderer, RouteData, RuntimeMode } from '../../../@types/astro';
import type { AstroConfig, ComponentInstance, Renderer, RouteData, RuntimeMode, SSRElement } from '../../../@types/astro';
import { LogOptions } from '../../logger.js';
import { fileURLToPath } from 'url';
import { getStylesForURL } from './css.js';
@ -48,14 +48,15 @@ export async function preload({ astroConfig, filePath, viteServer }: SSROptions)
/** use Vite to SSR */
export async function render(renderers: Renderer[], mod: ComponentInstance, ssrOpts: SSROptions): Promise<string> {
const { astroConfig, filePath, logging, mode, origin, pathname, route, routeCache, viteServer } = ssrOpts;
const legacy = astroConfig.buildOptions.legacyBuild;
// Add hoisted script tags
const scripts = createModuleScriptElementWithSrcSet(
astroConfig.buildOptions.experimentalStaticBuild && mod.hasOwnProperty('$$metadata') ? Array.from(mod.$$metadata.hoistedScriptPaths()) : []
!legacy && mod.hasOwnProperty('$$metadata') ? Array.from(mod.$$metadata.hoistedScriptPaths()) : []
);
// Inject HMR scripts
if (mod.hasOwnProperty('$$metadata') && mode === 'development' && astroConfig.buildOptions.experimentalStaticBuild) {
if (mod.hasOwnProperty('$$metadata') && mode === 'development' && !legacy) {
scripts.add({
props: { type: 'module', src: '/@vite/client' },
children: '',
@ -66,9 +67,31 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
});
}
// Pass framework CSS in as link tags to be appended to the page.
let links = new Set<SSRElement>();
if(!legacy) {
[...getStylesForURL(filePath, viteServer)].forEach((href) => {
if (mode === 'development' && svelteStylesRE.test(href)) {
scripts.add({
props: { type: 'module', src: href },
children: '',
});
} else {
links.add({
props: {
rel: 'stylesheet',
href,
'data-astro-injected': true,
},
children: '',
});
}
});
}
let content = await coreRender({
experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
links: new Set(),
legacyBuild: astroConfig.buildOptions.legacyBuild,
links,
logging,
markdownRender: astroConfig.markdownOptions.render,
mod,
@ -80,7 +103,7 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
// The legacy build needs these to remain unresolved so that vite HTML
// Can do the resolution. Without this condition the build output will be
// broken in the legacy build. This can be removed once the legacy build is removed.
if (astroConfig.buildOptions.experimentalStaticBuild) {
if (!astroConfig.buildOptions.legacyBuild) {
const [, resolvedPath] = await viteServer.moduleGraph.resolveUrl(s);
return resolvedPath;
} else {
@ -101,7 +124,7 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
const tags: vite.HtmlTagDescriptor[] = [];
// dev only: inject Astro HMR client
if (mode === 'development' && !astroConfig.buildOptions.experimentalStaticBuild) {
if (mode === 'development' && legacy) {
tags.push({
tag: 'script',
attrs: { type: 'module' },
@ -113,31 +136,34 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
}
// inject CSS
[...getStylesForURL(filePath, viteServer)].forEach((href) => {
if (mode === 'development' && svelteStylesRE.test(href)) {
tags.push({
tag: 'script',
attrs: { type: 'module', src: href },
injectTo: 'head',
});
} else {
tags.push({
tag: 'link',
attrs: {
rel: 'stylesheet',
href,
'data-astro-injected': true,
},
injectTo: 'head',
});
}
});
if(legacy) {
[...getStylesForURL(filePath, viteServer)].forEach((href) => {
if (mode === 'development' && svelteStylesRE.test(href)) {
tags.push({
tag: 'script',
attrs: { type: 'module', src: href },
injectTo: 'head',
});
} else {
tags.push({
tag: 'link',
attrs: {
rel: 'stylesheet',
href,
'data-astro-injected': true,
},
injectTo: 'head',
});
}
});
}
// add injected tags
content = injectTags(content, tags);
// run transformIndexHtml() in dev to run Vite dev transformations
if (mode === 'development' && !astroConfig.buildOptions.experimentalStaticBuild) {
if (mode === 'development' && astroConfig.buildOptions.legacyBuild) {
const relativeURL = filePath.href.replace(astroConfig.projectRoot.href, '/');
content = await viteServer.transformIndexHtml(relativeURL, content, pathname);
}

View file

@ -8,7 +8,7 @@ import { renderSlot } from '../../runtime/server/index.js';
import { warn, LogOptions } from '../logger.js';
export interface CreateResultArgs {
experimentalStaticBuild: boolean;
legacyBuild: boolean;
logging: LogOptions;
origin: string;
markdownRender: MarkdownRenderOptions;
@ -22,7 +22,7 @@ export interface CreateResultArgs {
}
export function createResult(args: CreateResultArgs): SSRResult {
const { experimentalStaticBuild, origin, markdownRender, params, pathname, renderers, resolve, site: buildOptionsSite } = args;
const { legacyBuild, origin, markdownRender, params, pathname, renderers, resolve, site: buildOptionsSite } = args;
// Create the result object that will be passed into the render function.
// This object starts here as an empty shell (not yet the result) but then
@ -45,7 +45,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
url,
},
resolve(path: string) {
if (experimentalStaticBuild) {
if (!legacyBuild) {
let extra = `This can be replaced with a dynamic import like so: await import("${path}")`;
if (isCSSRequest(path)) {
extra = `It looks like you are resolving styles. If you are adding a link tag, replace with this:
@ -116,7 +116,7 @@ ${extra}`
_metadata: {
renderers,
pathname,
experimentalStaticBuild,
legacyBuild
},
};

View file

@ -3,7 +3,6 @@ const STATUS_CODE_PAGE_REGEXP = /\/[0-9]{3}\/?$/;
/** Construct sitemap.xml given a set of URLs */
export function generateSitemap(pages: string[]): string {
// TODO: find way to respect <link rel="canonical"> URLs here
// TODO: find way to exclude pages from sitemap
// copy just in case original copy is needed

View file

@ -216,7 +216,7 @@ export function createRouteManifest({ config, cwd }: { config: AstroConfig; cwd?
basename,
ext,
parts,
file: slash(file),
file: file.replace(/\\/g, '/'),
isDir,
isIndex,
isPage,

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

@ -337,12 +337,14 @@ export function createAstro(filePathname: string, _site: string, projectRootStr:
};
}
const toAttributeString = (value: any) => String(value).replace(/&/g, '&#38;').replace(/"/g, '&#34;');
const toAttributeString = (value: any, shouldEscape = true) => shouldEscape ?
String(value).replace(/&/g, '&#38;').replace(/"/g, '&#34;') :
value;
const STATIC_DIRECTIVES = new Set(['set:html', 'set:text']);
// A helper used to turn expressions into attribute key/value
export function addAttribute(value: any, key: string) {
export function addAttribute(value: any, key: string, shouldEscape = true) {
if (value == null) {
return '';
}
@ -372,15 +374,15 @@ Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the
if (value === true && (key.startsWith('data-') || htmlBooleanAttributes.test(key))) {
return unescapeHTML(` ${key}`);
} else {
return unescapeHTML(` ${key}="${toAttributeString(value)}"`);
return unescapeHTML(` ${key}="${toAttributeString(value, shouldEscape)}"`);
}
}
// Adds support for `<Component {...value} />
export function spreadAttributes(values: Record<any, any>) {
export function spreadAttributes(values: Record<any, any>, shouldEscape = true) {
let output = '';
for (const [key, value] of Object.entries(values)) {
output += addAttribute(value, key);
output += addAttribute(value, key, shouldEscape);
}
return unescapeHTML(output);
}
@ -438,7 +440,7 @@ export async function renderPage(result: SSRResult, Component: AstroComponentFac
const styles = Array.from(result.styles)
.filter(uniqueElements)
.map((style) => {
const styleChildren = result._metadata.experimentalStaticBuild ? '' : style.children;
const styleChildren = !result._metadata.legacyBuild ? '' : style.children;
return renderElement('style', {
children: styleChildren,
@ -458,12 +460,12 @@ 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)
.filter(uniqueElements)
.map((link) => renderElement('link', link));
.map((link) => renderElement('link', link, false));
// inject styles & scripts at end of <head>
let headPos = template.indexOf('</head>');
@ -513,7 +515,7 @@ function getHTMLElementName(constructor: typeof HTMLElement) {
return assignedName;
}
function renderElement(name: string, { props: _props, children = '' }: SSRElement) {
function renderElement(name: string, { props: _props, children = '' }: SSRElement, shouldEscape = true) {
// Do not print `hoist`, `lang`, `global`
const { lang: _, 'data-astro-id': astroId, 'define:vars': defineVars, ...props } = _props;
if (defineVars) {
@ -530,5 +532,5 @@ function renderElement(name: string, { props: _props, children = '' }: SSRElemen
children = defineScriptVars(defineVars) + '\n' + children;
}
}
return `<${name}${spreadAttributes(props)}>${children}</${name}>`;
return `<${name}${spreadAttributes(props, shouldEscape)}>${children}</${name}>`;
}

View file

@ -13,6 +13,7 @@ interface ComponentMetadata {
interface CreateMetadataOptions {
modules: ModuleInfo[];
hydratedComponents: any[];
clientOnlyComponents: any[];
hydrationDirectives: Set<string>;
hoisted: any[];
}
@ -22,6 +23,7 @@ export class Metadata {
public modules: ModuleInfo[];
public hoisted: any[];
public hydratedComponents: any[];
public clientOnlyComponents: any[];
public hydrationDirectives: Set<string>;
private metadataCache: Map<any, ComponentMetadata | null>;
@ -30,6 +32,7 @@ export class Metadata {
this.modules = opts.modules;
this.hoisted = opts.hoisted;
this.hydratedComponents = opts.hydratedComponents;
this.clientOnlyComponents = opts.clientOnlyComponents;
this.hydrationDirectives = opts.hydrationDirectives;
this.mockURL = new URL(filePathname, 'http://example.com');
this.metadataCache = new Map<any, ComponentMetadata | null>();
@ -66,6 +69,19 @@ export class Metadata {
}
}
*clientOnlyComponentPaths() {
const found = new Set<string>();
for (const metadata of this.deepMetadata()) {
for (const component of metadata.clientOnlyComponents) {
const path = metadata.resolvePath(component);
if (path && !found.has(path)) {
found.add(path);
yield path;
}
}
}
}
/**
* Gets all of the hydration specifiers used within this component.
*/

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;
}

View file

@ -54,15 +54,15 @@ async function compile(config: AstroConfig, filename: string, source: string, vi
sourcefile: filename,
sourcemap: 'both',
internalURL: 'astro/internal',
experimentalStaticExtraction: config.buildOptions.experimentalStaticBuild,
experimentalStaticExtraction: !config.buildOptions.legacyBuild,
// TODO add experimental flag here
preprocessStyle: async (value: string, attrs: Record<string, string>) => {
const lang = `.${attrs?.lang || 'css'}`.toLowerCase();
try {
// In the static build, grab any @import as CSS dependencies for HMR.
if (config.buildOptions.experimentalStaticBuild) {
value = value.replace(/(?:@import)\s(?:url\()?\s?["\'](.*?)["\']\s?\)?(?:[^;]*);?/gi, (match, spec) => {
if (!config.buildOptions.legacyBuild) {
value.replace(/(?:@import)\s(?:url\()?\s?["\'](.*?)["\']\s?\)?(?:[^;]*);?/gi, (match, spec) => {
rawCSSDeps.add(spec);
// If the language is CSS: prevent `@import` inlining to prevent scoping of imports.
// Otherwise: Sass, etc. need to see imports for variables, so leave in for their compiler to handle.

View file

@ -15,6 +15,11 @@ describe('CSS', function () {
fixture = await loadFixture({
projectRoot: './fixtures/0-css/',
renderers: ['@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
vite: {
build: {
assetsInlineLimit: 0
}
}
});
});
@ -31,7 +36,7 @@ describe('CSS', function () {
// get bundled CSS (will be hashed, hence DOM query)
const html = await fixture.readFile('/index.html');
$ = cheerio.load(html);
const bundledCSSHREF = $('link[rel=stylesheet][href^=./assets/]').attr('href');
const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href');
bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
});
@ -47,7 +52,8 @@ describe('CSS', function () {
expect(el2.attr('class')).to.equal(`visible ${scopedClass}`);
// 2. check CSS
expect(bundledCSS).to.include(`.blue.${scopedClass}{color:#b0e0e6}.color\\:blue.${scopedClass}{color:#b0e0e6}.visible.${scopedClass}{display:block}`);
const expected = `.blue.${scopedClass}{color:#b0e0e6}.color\\\\:blue.${scopedClass}{color:#b0e0e6}.visible.${scopedClass}{display:block}`;
expect(bundledCSS).to.include(expected);
});
it('No <style> skips scoping', async () => {
@ -60,7 +66,8 @@ describe('CSS', function () {
});
it('Using hydrated components adds astro-root styles', async () => {
expect(bundledCSS).to.include('display:contents');
const inline = $('style').html();
expect(inline).to.include('display: contents');
});
it('<style lang="sass">', async () => {
@ -217,7 +224,7 @@ describe('CSS', function () {
it('<style>', async () => {
const el = $('#svelte-css');
const classes = el.attr('class').split(' ');
const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
const scopedClass = classes.find((name) => name !== 'svelte-css' && /^svelte-[A-Za-z0-9-]+/.test(name));
// 1. check HTML
expect(el.attr('class')).to.include('svelte-css');
@ -229,7 +236,7 @@ describe('CSS', function () {
it('<style lang="sass">', async () => {
const el = $('#svelte-sass');
const classes = el.attr('class').split(' ');
const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
const scopedClass = classes.find((name) => name !== 'svelte-sass' && /^svelte-[A-Za-z0-9-]+/.test(name));
// 1. check HTML
expect(el.attr('class')).to.include('svelte-sass');
@ -241,7 +248,7 @@ describe('CSS', function () {
it('<style lang="scss">', async () => {
const el = $('#svelte-scss');
const classes = el.attr('class').split(' ');
const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name));
const scopedClass = classes.find((name) => name !== 'svelte-scss' && /^svelte-[A-Za-z0-9-]+/.test(name));
// 1. check HTML
expect(el.attr('class')).to.include('svelte-scss');
@ -272,11 +279,6 @@ describe('CSS', function () {
expect((await fixture.fetch(href)).status).to.equal(200);
});
it('resolves CSS in src/', async () => {
const href = $('link[href$="linked.css"]').attr('href');
expect((await fixture.fetch(href)).status).to.equal(200);
});
// Skipped until upstream fix lands
// Our fix: https://github.com/withastro/astro/pull/2106
// OG Vite PR: https://github.com/vitejs/vite/pull/5940

View file

@ -11,7 +11,14 @@ describe('Assets', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-assets/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-assets/',
vite: {
build: {
assetsInlineLimit: 0
}
}
});
await fixture.build();
});
@ -19,7 +26,7 @@ describe('Assets', () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
const imgPath = $('img').attr('src');
const data = await fixture.readFile('/' + imgPath);
const data = await fixture.readFile( imgPath);
expect(!!data).to.equal(true);
});
@ -29,7 +36,7 @@ describe('Assets', () => {
const srcset = $('img').attr('srcset');
const candidates = matchSrcset(srcset);
const match = candidates.find((a) => a.density === 2);
const data = await fixture.readFile('/' + match.url);
const data = await fixture.readFile(match.url);
expect(!!data).to.equal(true);
});
@ -39,14 +46,14 @@ describe('Assets', () => {
const srcset = $('img').attr('srcset');
const candidates = matchSrcset(srcset);
const match = candidates.find((a) => a.density === 3);
const data = await fixture.readFile('/' + match.url);
const data = await fixture.readFile(match.url);
expect(!!data).to.equal(true);
});
it('built image from an import specifier', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
const src = '/' + $('#import-no-url').attr('src');
const src = $('#import-no-url').attr('src');
const data = await fixture.readFile(src);
expect(!!data).to.equal(true);
});
@ -54,7 +61,7 @@ describe('Assets', () => {
it('built image from an import specifier using ?url', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
const src = '/' + $('#import-url').attr('src');
const src = $('#import-url').attr('src');
const data = await fixture.readFile(src);
expect(!!data).to.equal(true);
});

View file

@ -7,7 +7,9 @@ describe('Astro basics', () => {
let previewServer;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-basic/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-basic/',
});
await fixture.build();
previewServer = await fixture.preview();
});

View file

@ -6,7 +6,9 @@ describe('Client only components', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-client-only/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-client-only/',
});
await fixture.build();
});
@ -16,19 +18,10 @@ describe('Client only components', () => {
// test 1: <astro-root> is empty
expect($('astro-root').html()).to.equal('');
const src = $('script').attr('src');
const $script = $('script');
const script = $script.html();
const script = await fixture.readFile(src);
// test 2: svelte renderer is on the page
const exp = /import\("(.\/client.*)"\)/g;
let match, svelteRenderer;
while ((match = exp.exec(script))) {
svelteRenderer = match[1].replace(/^\./, '/assets/');
}
expect(svelteRenderer).to.be.ok;
// test 3: can load svelte renderer
const svelteClient = await fixture.readFile(svelteRenderer);
expect(svelteClient).to.be.ok;
expect(/import\(".\/PersistentCounter.*/g.test(script)).to.be.ok;
});
});

View file

@ -1,56 +0,0 @@
/**
* UNCOMMENT: add support for functional components in frontmatter
import { expect } from 'chai';
import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-components/' });
await fixture.build();
});
// TODO: add support for functional components in frontmatter
describe('Components tests', () => {
it('Astro components are able to render framework components', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
// test 1: Renders Astro component
const $astro = $('#astro');
expect($astro.children()).to.have.lengthOf(3);
// test 2: Renders React component
const $react = $('#react');
expect($react).not.to.have.lengthOf(0);
// test 3: Renders Vue component
const $vue = $('#vue');
expect($vue).not.to.have.lengthOf(0);
// test 4: Renders Svelte component
const $svelte = $('#svelte');
expect($svelte).not.to.have.lengthOf(0);
});
it('Allows Components defined in frontmatter', async () => {
const html = await fixture.readFile('/frontmatter-component/index.html');
const $ = cheerio.load(html);
expect($('h1')).to.have.lengthOf(1);
});
it('Still throws an error for undefined components', async () => {
const result = await fixture.readFile('/undefined-component/index.html');
expect(result.status).to.equal(500);
});
it('Client attrs not added', async () => {
const html = await fixture.readFile('/client/index.html');
expect(html).not.to.include(`"client:load": true`);
});
});
*/
it.skip('is skipped', () => {});

View file

@ -6,7 +6,9 @@ describe('CSS Bundling (ESM import)', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling-import/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-css-bundling-import/',
});
await fixture.build();
});
@ -32,8 +34,7 @@ describe('CSS Bundling (ESM import)', () => {
expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:#00f}'));
});
// TODO: re-enable this
it.skip('no empty CSS files', async () => {
it('no empty CSS files', async () => {
for (const page of ['/page-1/index.html', '/page-2/index.html']) {
const html = await fixture.readFile(page);
const $ = cheerio.load(html);

View file

@ -1,38 +0,0 @@
import { expect } from 'chai';
import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('nested layouts', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling-nested-layouts/' });
await fixture.build();
});
it('page-1 has all CSS', async () => {
const html = await fixture.readFile('/page-1/index.html');
const $ = cheerio.load(html);
const stylesheets = $('link[rel=stylesheet]')
.toArray()
.map((el) => el.attribs.href);
// page-one.[hash].css exists
expect(stylesheets.some((href) => /page-one\.\w+\.css/.test(href))).to.be.true;
});
it('page-2 has all CSS', async () => {
const html = await fixture.readFile('/page-2/index.html');
const $ = cheerio.load(html);
const stylesheets = $('link[rel=stylesheet]')
.toArray()
.map((el) => el.attribs.href);
// page-one.[hash].css exists
expect(stylesheets.some((href) => /page-one\.\w+\.css/.test(href))).to.be.true;
// page-2.[hash].css exists
expect(stylesheets.some((href) => /page-2\.\w+\.css/.test(href))).to.be.true;
});
});

View file

@ -5,11 +5,9 @@ import { loadFixture } from './test-utils.js';
// note: the hashes should be deterministic, but updating the file contents will change hashes
// be careful not to test that the HTML simply contains CSS, because it always will! filename and quanity matter here (bundling).
const EXPECTED_CSS = {
'/index.html': ['./assets/index', './assets/typography'], // dont match hashes, which change based on content
'/one/index.html': ['../assets/one'],
'/two/index.html': ['../assets/two', '../assets/typography'],
'/preload/index.html': ['../assets/preload'],
'/preload-merge/index.html': ['../assets/preload-merge'],
'/index.html': ['/assets/index'], // dont match hashes, which change based on content
'/one/index.html': ['/assets/one'],
'/two/index.html': ['/assets/two'],
};
const UNEXPECTED_CSS = ['/src/components/nav.css', '../css/typography.css', '../css/colors.css', '../css/page-index.css', '../css/page-one.css', '../css/page-two.css'];
@ -17,7 +15,9 @@ describe('CSS Bundling', function () {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-css-bundling/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-css-bundling/',
});
await fixture.build({ mode: 'production' });
});
@ -43,21 +43,7 @@ describe('CSS Bundling', function () {
expect(link).to.have.lengthOf(0);
}
// test 3: preload tags was not removed and attributes was preserved
if (filepath === '/preload/index.html') {
const stylesheet = $('link[rel="stylesheet"][href^="../assets/preload"]');
const preload = $('link[rel="preload"][href^="../assets/preload"]');
expect(stylesheet[0].attribs.media).to.equal('print');
expect(preload).to.have.lengthOf(1); // Preload tag was removed
}
// test 4: preload tags was not removed and attributes was preserved
if (filepath === '/preload-merge/index.html') {
const preload = $('link[rel="preload"]');
expect(preload).to.have.lengthOf(1);
}
// test 5: assert all bundled CSS was built and contains CSS
// test 3: assert all bundled CSS was built and contains CSS
for (const url of builtCSS.keys()) {
const css = await fixture.readFile(url);
expect(css).to.be.ok;

View file

@ -6,7 +6,9 @@ describe('Dynamic components', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-dynamic/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-dynamic/',
});
await fixture.build();
});
@ -14,7 +16,7 @@ describe('Dynamic components', () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('script').length).to.eq(1);
expect($('script').length).to.eq(2);
});
it('Loads pages using client:media hydrator', async () => {
@ -23,11 +25,7 @@ describe('Dynamic components', () => {
const $ = cheerio.load(html);
// test 1: static value rendered
let js = await fixture.readFile(new URL($('script').attr('src'), root).pathname);
expect(js).to.include(`value:"(max-width: 700px)"`);
// test 2: dynamic value rendered
expect(js).to.include(`value:"(max-width: 600px)"`);
expect($('script').length).to.equal(2); // One for each
});
it('Loads pages using client:only hydrator', async () => {

View file

@ -5,7 +5,10 @@ describe('Environment Variables', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-envs/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-envs/',
buildOptions: { legacyBuild: true } // TODO make this test work without legacyBuild
});
await fixture.build();
});

View file

@ -10,7 +10,7 @@ describe('Astro.*', () => {
projectRoot: './fixtures/astro-global/',
buildOptions: {
site: 'https://mysite.dev/blog/',
sitemap: false,
sitemap: false
},
});
await fixture.build();
@ -48,13 +48,6 @@ describe('Astro.*', () => {
expect($('#site').attr('href')).to.equal('https://mysite.dev/blog/');
});
it('Astro.resolve built', async () => {
const html = await fixture.readFile('/resolve/index.html');
const $ = cheerio.load(html);
expect($('img').attr('src')).to.include('assets/penguin.ccd44411.png'); // Main src/images
expect($('#inner-child img').attr('src')).to.include('assets/penguin.b9ab122a.png');
});
it('Astro.fetchContent() returns the correct "url" property, including buildOptions.site subpath', async () => {
const html = await fixture.readFile('/posts/1/index.html');
const $ = cheerio.load(html);

View file

@ -7,7 +7,9 @@ describe('Partial HTML ', async () => {
let devServer;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-partial-html/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-partial-html/',
});
devServer = await fixture.startDevServer();
});
@ -23,7 +25,12 @@ describe('Partial HTML ', async () => {
expect(html).to.match(/^<!DOCTYPE html/);
// test 2: correct CSS present
const css = $('style[astro-style]').html();
const link = $('link').attr('href');
const css = await fixture.fetch(link, {
headers: {
accept: 'text/css'
}
}).then(res => res.text());
expect(css).to.match(/\.astro-[^{]+{color:red;}/);
});

View file

@ -7,7 +7,14 @@ describe('Scripts (hoisted and not)', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-scripts/' });
fixture = await loadFixture({
projectRoot: './fixtures/astro-scripts/',
vite: {
build: {
assetsInlineLimit: 0
}
}
});
await fixture.build();
});
@ -38,8 +45,8 @@ describe('Scripts (hoisted and not)', () => {
// test 2: attr removed
expect($('script').attr('data-astro')).to.equal(undefined);
let entryURL = path.join('inline', $('script').attr('src'));
let inlineEntryJS = await fixture.readFile(entryURL);
const entryURL = $('script').attr('src');
const inlineEntryJS = await fixture.readFile(entryURL);
// test 3: the JS exists
expect(inlineEntryJS).to.be.ok;
@ -53,7 +60,7 @@ describe('Scripts (hoisted and not)', () => {
expect($('script')).to.have.lengthOf(2);
let el = $('script').get(1);
let entryURL = path.join('external', $(el).attr('src'));
let entryURL = $(el).attr('src');
let externalEntryJS = await fixture.readFile(entryURL);
// test 2: the JS exists

View file

@ -1,27 +0,0 @@
import { expect } from 'chai';
import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Node builtins', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/builtins/' });
await fixture.build();
});
it('Can be used with the node: prefix', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('#version').text()).to.equal('1.2.0');
expect($('#dep-version').text()).to.equal('0.1.0');
});
it('Can also be used with the non-prefixed version', async () => {
const html = await fixture.readFile('/bare/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('true');
});
});

View file

@ -1,47 +0,0 @@
import { expect } from 'chai';
import { cli } from './test-utils.js';
import { promises as fs } from 'fs';
import { fileURLToPath } from 'url';
describe('astro cli', () => {
it('astro', async () => {
const proc = await cli();
expect(proc.stdout).to.include('astro - Futuristic web development tool');
});
it('astro --version', async () => {
const pkgURL = new URL('../package.json', import.meta.url);
const pkgVersion = await fs.readFile(pkgURL, 'utf8').then((data) => JSON.parse(data).version);
const proc = await cli('--version');
expect(proc.stdout).to.equal(pkgVersion);
});
it('astro dev', async () => {
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL));
let stdout = '';
for await (const chunk of proc.stdout) {
stdout += chunk;
if (chunk.includes('Local:')) break;
}
proc.kill();
expect(stdout).to.include('Server started');
});
it('astro build', async () => {
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
const proc = await cli('build', '--project-root', fileURLToPath(projectRootURL));
expect(proc.stdout).to.include('Done');
});
});

View file

@ -47,8 +47,8 @@ describe('Custom Elements', () => {
expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1);
// Hydration
// test 3: Component and polyfill scripts bundled together
expect($('script[type=module]')).to.have.lengthOf(1);
// test 3: Component and polyfill scripts bundled separately
expect($('script[type=module]')).to.have.lengthOf(2);
});
it('Polyfills are added even if not hydrating', async () => {

View file

@ -19,7 +19,7 @@ import VueScoped from '../components/VueScoped.vue';
import VueScss from '../components/VueScss.vue';
import ReactDynamic from '../components/ReactDynamic.jsx';
import importedUrl from '../styles/imported-url.css?url';
import '../styles/imported-url.css';
---
<html>
@ -36,10 +36,15 @@ import importedUrl from '../styles/imported-url.css?url';
}
</style>
<link rel="stylesheet" type="text/css" href="/global.css">
<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/linked.css')}>
<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/linked.scss')}>
<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/linked.sass')}>
<link rel="stylesheet" type="text/css" href={importedUrl}>
<style>
@import '../styles/linked.css';
</style>
<style lang="scss">
@import '../styles/linked.scss';
</style>
<style lang="sass">
@import '../styles/linked.sass'
</style>
</head>
<body>
<div class="wrapper">

View file

@ -9,7 +9,7 @@ import p2Url from '../images/penguin2.jpg?url';
</style>
<body>
<h1>Icons</h1>
<img src={Astro.resolve('../images/twitter.png')} srcset={`${Astro.resolve('../images/twitter.png')} 1x, ${Astro.resolve('../images/twitter@2x.png')} 2x, ${Astro.resolve('../images/twitter@3x.png')} 3x`} />
<img src={(await import('../images/twitter.png')).default} srcset={`${(await import('../images/twitter.png')).default} 1x, ${(await import('../images/twitter@2x.png')).default} 2x, ${(await import('../images/twitter@3x.png')).default} 3x`} />
<img srcset="https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg, https://ik.imagekit.io/demo/tr:w-450,h-450/medium_cafe_B1iTdD0C.jpg 600w, https://ik.imagekit.io/demo/tr:w-600,h-600/medium_cafe_B1iTdD0C.jpg 800w">
<img srcset="https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg, https://ik.imagekit.io/demo/tr:w-450,h-450/medium_cafe_B1iTdD0C.jpg 1.5x, https://ik.imagekit.io/demo/tr:w-600,h-600/medium_cafe_B1iTdD0C.jpg 2x">
<!--

View file

@ -1,11 +0,0 @@
---
import ReactComponent from './Component.jsx';
import VueComponent from './Component.vue';
import SvelteComponent from './Component.svelte';
---
<div id="astro">
<ReactComponent />
<VueComponent />
<SvelteComponent />
</div>

View file

@ -1,5 +0,0 @@
import { h } from 'preact';
export default function PreactComponent({ children }) {
return <div id="preact">{children}</div>;
}

View file

@ -1,3 +0,0 @@
<div id="svelte">
<slot />
</div>

View file

@ -1,9 +0,0 @@
<template>
<div id="vue">
<slot />
</div>
</template>
<script>
export default {}
</script>

View file

@ -1,6 +0,0 @@
---
export interface Props {
test: string;
}
---
<h1 id="direct-props-h1">{props.test}</h1>

View file

@ -1,9 +0,0 @@
---
import SvelteComponent from '../components/Component.svelte';
---
<html>
<head><title>Components</title></head>
<body>
<SvelteComponent client:load />
</body>
</html>

View file

@ -1,6 +0,0 @@
---
const { level = 1 } = Astro.props;
const Element = `h${level}`;
---
<Element>Hello world!</Element>

View file

@ -1,9 +0,0 @@
---
import AstroComponent from '../components/Component.astro';
---
<html>
<head><title>Components</title></head>
<body>
<AstroComponent />
</body>
</html>

View file

@ -1,4 +0,0 @@
---
import PropsComponent from "../components/Props-Component.astro"
---
<PropsComponent test="test string"/>

View file

@ -1,11 +0,0 @@
---
function FnComponent() {
const lame = 'ugh';
return <h2>Hey</h2>
}
const Defined = 'h1'
---
<FnComponent />
<Defined>Hello world!</Defined>
<Undefined />

View file

@ -1,27 +0,0 @@
---
import "../styles/site.css"
const {title} = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width" />
<title>{title}</title>
<style>
</style>
</head>
<body>
<ul>
<li><a href="/page-1">Page 1</a></li>
<li><a href="/page-2">Page 2</a></li>
<!-- <li><a href="/page-2-reduced-layout">Page 2 reduced layout</a></li> -->
</ul>
<slot></slot>
</body>
</html>

View file

@ -1,12 +0,0 @@
---
import BaseLayout from "./BaseLayout.astro"
import "../styles/page-one.css"
const {title} = Astro.props;
---
<BaseLayout title={title}>
<main id="page">
<slot></slot>
</main>
</BaseLayout>

View file

@ -1,15 +0,0 @@
---
import PageLayout from "../layouts/PageLayout.astro"
const date = new Date();
---
<PageLayout title="Page 1">
<h1>Page 1</h1>
<p>This page has styling in dev-server. But the built page has no styling. </p>
<p>Check <code>dist/page-1/index.html</code>. There are no stylesheets imported.</p>
<p>Additionally, there is an empty js file in the <code>dist/assets</code> folder. Thankfully the file is not required by any page.</p>
<p>Execute the build <code>npm run build</code> and preview it <code>npx http-server dist/</code> at <a href="https://github-qoihup--8080.local.webcontainer.io/page-1/">https://github-qoihup--8080.local.webcontainer.io/page-1/</a></p>
<p>Date: {date}</p>
</PageLayout>

View file

@ -1,9 +0,0 @@
---
import PageLayout from "../layouts/PageLayout.astro"
import "../styles/page-two.css"
---
<PageLayout title="Page 2">
<h1>Page 2</h1>
<p>Nothing to see here. Check <a href="/page-1">Page 1</a></p>
</PageLayout>

View file

@ -1,3 +0,0 @@
p {
color: blue;
}

View file

@ -1,3 +0,0 @@
p {
color: green;
}

View file

@ -1,7 +0,0 @@
p {
color: red;
}
h1 {
outline: 1px solid red;
}

View file

@ -4,9 +4,11 @@ import Nav from '../components/Nav.astro';
<html>
<head>
<link rel="stylesheet" href={Astro.resolve('../css/typography.css')}>
<link rel="stylesheet" href={Astro.resolve('../css/colors.css')}>
<link rel="stylesheet" href={Astro.resolve('../css/page-index.css')}>
<style>
@import '../css/typography.css';
@import '../css/colors.css';
@import '../css/page-index.css';
</style>
</head>
<body>
<Nav />

View file

@ -4,8 +4,10 @@ import Nav from '../components/Nav.astro';
<html>
<head>
<link rel="stylesheet" href={Astro.resolve('../css/typography.css')} />
<link rel="stylesheet" href={Astro.resolve('../css/page-one.css')} />
<style>
@import '../css/typography.css';
@import '../css/page-one.css';
</style>
</head>
<body>
<Nav />

View file

@ -1,17 +0,0 @@
---
import Nav from '../components/Nav.astro';
---
<html>
<head>
<link rel="preload" as="style" href={Astro.resolve('../css/page-preload-merge.css')} />
<link rel="preload" as="style" href={Astro.resolve('../css/page-preload-merge-2.css')} />
<link rel="stylesheet" href={Astro.resolve('../css/page-preload-merge.css')} />
<link rel="stylesheet" href={Astro.resolve('../css/page-preload-merge-2.css')} />
</head>
<body>
<Nav />
<h1>Preload merge page</h1>
</body>
</html>

View file

@ -1,14 +0,0 @@
---
import Nav from '../components/Nav.astro';
---
<html>
<head>
<link rel="preload" as="style" href={Astro.resolve('../css/page-preload.css')} />
<link rel="stylesheet" href={Astro.resolve('../css/page-preload.css')} media="print" onload="this.media='all'" />
</head>
<body>
<Nav />
<h1>Preload page</h1>
</body>
</html>

View file

@ -4,9 +4,11 @@ import Nav from '../components/Nav.astro';
<html>
<head>
<link rel="stylesheet" href={Astro.resolve('../css/typography.css')} />
<link rel="stylesheet" href={Astro.resolve('../css/colors.css')} />
<link rel="stylesheet" href={Astro.resolve('../css/page-two.css')} />
<style>
@import '../css/typography.css';
@import '../css/colors.css';
@import '../css/page-two.css';
</style>
</head>
<body>
<Nav />

View file

@ -1,5 +0,0 @@
---
const penguinUrl = Astro.resolve('../images/penguin.png');
---
<img src={penguinUrl} />
<img src="../images/penguin.png" />

View file

@ -1,6 +0,0 @@
---
const penguinUrl = Astro.resolve('./images/penguin.png');
---
<div id="inner-child">
<img src={penguinUrl} />
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

View file

@ -1,14 +0,0 @@
---
import Child from '../components/ChildResolve.astro';
import InnerChild from '../components/nested/InnerChild.astro';
---
<html>
<head>
<title>Testing</title>
</head>
<body>
<Child />
<InnerChild />
</body>
</html>

View file

@ -1 +1,4 @@
<script src={Astro.resolve("../scripts/no_hoist_nonmodule.js")}></script>
---
import url from "../scripts/no_hoist_nonmodule.js?url"
---
<script src={url}></script>

View file

@ -1 +1,4 @@
<script type="module" src={Astro.resolve("../scripts/no_hoist_module.js")}></script>
---
import url from '../scripts/no_hoist_module.js?url';
---
<script type="module" src={url}></script>

View file

@ -1 +1 @@
<script hoist type="module" src={Astro.resolve("../scripts/something.js")}></script>
<script hoist type="module" src="../scripts/something.js"></script>

View file

@ -1 +1 @@
<script hoist type="module" src={Astro.resolve("../scripts/another_external.js")}></script>
<script hoist type="module" src="../scripts/another_external.js"></script>

View file

@ -1,8 +0,0 @@
{
"name": "@astrojs/astro-test-builtins",
"version": "1.2.0",
"private": true,
"dependencies": {
"@astrojs/astro-test-builtins-dep": "file:./packages/dep"
}
}

View file

@ -1,8 +0,0 @@
# @astrojs/astro-test-builtins-dep
## 0.1.0
### Minor Changes
- [#2202](https://github.com/withastro/astro/pull/2202) [`45cea6ae`](https://github.com/withastro/astro/commit/45cea6aec5a310fed4cb8da0d96670d6b99a2539) Thanks [@jonathantneal](https://github.com/jonathantneal)! - Officially drop support for Node v12. The minimum supported version is now Node v14.15+,

View file

@ -1,10 +0,0 @@
import fs from 'fs';
const readFile = fs.promises.readFile;
export async function readJson(path) {
const json = await readFile(path, 'utf-8');
const data = JSON.parse(json);
return data;
}

View file

@ -1,7 +0,0 @@
{
"name": "@astrojs/astro-test-builtins-dep",
"version": "0.1.0",
"private": true,
"module": "main.js",
"main": "main.js"
}

View file

@ -1,9 +0,0 @@
---
import { promises as fs } from 'node:fs';
const url = new URL('../../package.json', import.meta.url);
const json = await fs.readFile(url, 'utf-8');
const data = JSON.parse(json);
---
<span id="version">{data.version}</span>

View file

@ -1,12 +0,0 @@
---
import { builtinModules } from 'module';
---
<html>
<head>
<title>Bare node import</title>
</head>
<body>
<h1>{Array.isArray(builtinModules)}</h1>
</body>
</html>

View file

@ -1,16 +0,0 @@
---
import Version from '../components/Version.astro';
import { readJson } from '@astrojs/astro-test-builtins-dep';
const depPath = new URL('../../packages/dep/package.json', import.meta.url);
const title = 'My App';
const depVersion = (await readJson(depPath)).version;
---
<title>{title}</title>
<h1>{title}</h1>
<Version />
<span id="dep-version">{depVersion}</span>

View file

@ -4,9 +4,12 @@ import JSX from '../components/Solid.jsx';
import Svelte from '../components/Svelte.svelte';
import Vue from '../components/Vue.vue';
---
<html>
<head>
<link rel="stylesheet" type="text/css" href="/global.css">
<link rel="stylesheet" type="text/css" href={Astro.resolve('../styles/linked.css')}>
<style>
@import '../styles/linked.css';
</style>
<style>
.astro-page {
appearance: none;
@ -22,3 +25,5 @@ import Vue from '../components/Vue.vue';
<Vue />
</div>
</body>
</html>

View file

@ -18,6 +18,9 @@ describe('LitElement test', function () {
fixture = await loadFixture({
projectRoot: './fixtures/lit-element/',
renderers: ['@astrojs/renderer-lit'],
buildOptions: {
legacyBuild: true
}
});
await fixture.build();
});

View file

@ -18,7 +18,7 @@ describe('PostCSS', () => {
// get bundled CSS (will be hashed, hence DOM query)
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
const bundledCSSHREF = $('link[rel=stylesheet][href^=./assets/]').attr('href');
const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href');
bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
});
@ -30,10 +30,6 @@ describe('PostCSS', () => {
expect(bundledCSS).to.match(new RegExp(`.astro-component.astro-[^{]+${PREFIXED_CSS}`));
});
it('works in <link>', () => {
expect(bundledCSS).to.match(new RegExp(`.a-n${PREFIXED_CSS}`));
});
it('works in JSX', () => {
expect(bundledCSS).to.match(new RegExp(`.solid${PREFIXED_CSS}`));
});

View file

@ -17,7 +17,7 @@ describe('Remote CSS', () => {
const $ = cheerio.load(html);
const relPath = $('link').attr('href');
const css = await fixture.readFile('/' + relPath);
const css = await fixture.readFile(relPath);
expect(css).to.match(/https:\/\/unpkg.com\/open-props/);
expect(css).to.match(/body/);

View file

@ -23,12 +23,12 @@ describe("Static build - pageUrlFormat: 'file'", () => {
});
it('Builds pages in root', async () => {
const html = await fixture.readFile('/subpath/one.html');
const html = await fixture.readFile('/one.html');
expect(html).to.be.a('string');
});
it('Builds pages in subfolders', async () => {
const html = await fixture.readFile('/subpath/sub/page.html');
const html = await fixture.readFile('/sub/page.html');
expect(html).to.be.a('string');
});
});

View file

@ -25,12 +25,12 @@ describe('Static build', () => {
});
it('Builds out .astro pages', async () => {
const html = await fixture.readFile('/subpath/index.html');
const html = await fixture.readFile('/index.html');
expect(html).to.be.a('string');
});
it('can build pages using fetchContent', async () => {
const html = await fixture.readFile('/subpath/index.html');
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
const link = $('.posts a');
const href = link.attr('href');
@ -38,18 +38,18 @@ describe('Static build', () => {
});
it('Builds out .md pages', async () => {
const html = await fixture.readFile('/subpath/posts/thoughts/index.html');
const html = await fixture.readFile('/posts/thoughts/index.html');
expect(html).to.be.a('string');
});
it('Builds out .json files', async () => {
const content = await fixture.readFile('/subpath/company.json').then((text) => JSON.parse(text));
const content = await fixture.readFile('/company.json').then((text) => JSON.parse(text));
expect(content.name).to.equal('Astro Technology Company');
expect(content.url).to.equal('https://astro.build/');
});
it('Builds out async .json files', async () => {
const content = await fixture.readFile('/subpath/posts.json').then((text) => JSON.parse(text));
const content = await fixture.readFile('/posts.json').then((text) => JSON.parse(text));
expect(Array.isArray(content)).to.equal(true);
expect(content).deep.equal([
{
@ -67,20 +67,20 @@ describe('Static build', () => {
const slugs = ['thing1', 'thing2'];
for (const slug of slugs) {
const content = await fixture.readFile(`/subpath/data/${slug}.json`).then((text) => JSON.parse(text));
const content = await fixture.readFile(`/data/${slug}.json`).then((text) => JSON.parse(text));
expect(content.name).to.equal('Astro Technology Company');
expect(content.url).to.equal('https://astro.build/');
expect(content.slug).to.equal(slug);
}
});
function createFindEvidence(expected) {
function createFindEvidence(expected, prefix) {
return async function findEvidence(pathname) {
const html = await fixture.readFile(pathname);
const $ = cheerio.load(html);
const links = $('link[rel=stylesheet]');
for (const link of links) {
const href = $(link).attr('href');
const href = $(link).attr('href').slice('/subpath'.length);
const data = await fixture.readFile(addLeadingSlash(href));
if (expected.test(data)) {
return true;
@ -95,7 +95,7 @@ describe('Static build', () => {
const findEvidence = createFindEvidence(/height:( )*45vw/);
it('Page level CSS is added', async () => {
const found = await findEvidence('/subpath/index.html');
const found = await findEvidence('/index.html');
expect(found).to.equal(true, 'Did not find page-level CSS on this page');
});
});
@ -104,12 +104,12 @@ describe('Static build', () => {
const findEvidence = createFindEvidence(/var\(--c\)/);
it('Included on the index page', async () => {
const found = await findEvidence('/subpath/index.html');
const found = await findEvidence('/index.html');
expect(found).to.equal(true, 'Did not find shared CSS on this page');
});
it('Included on a md page', async () => {
const found = await findEvidence('/subpath/posts/thoughts/index.html');
const found = await findEvidence('/posts/thoughts/index.html');
expect(found).to.equal(true, 'Did not find shared CSS on this page');
});
});
@ -118,30 +118,30 @@ describe('Static build', () => {
const findEvidence = createFindEvidence(/var\(--c-black\)/);
it('Is included in the index CSS', async () => {
const found = await findEvidence('/subpath/index.html');
const found = await findEvidence('/index.html');
expect(found).to.equal(true, 'Did not find shared CSS module code');
});
});
describe('Hoisted scripts', () => {
it('Get bundled together on the page', async () => {
const html = await fixture.readFile('/subpath/hoisted/index.html');
const html = await fixture.readFile('/hoisted/index.html');
const $ = cheerio.load(html);
expect($('script[type="module"]').length).to.equal(1, 'hoisted script added');
});
it('Do not get added to the wrong page', async () => {
const hoistedHTML = await fixture.readFile('/subpath/hoisted/index.html');
const hoistedHTML = await fixture.readFile('/hoisted/index.html');
const $ = cheerio.load(hoistedHTML);
const href = $('script[type="module"]').attr('src');
const indexHTML = await fixture.readFile('/subpath/index.html');
const indexHTML = await fixture.readFile('/index.html');
const $$ = cheerio.load(indexHTML);
expect($$(`script[src="${href}"]`).length).to.equal(0, 'no script added to different page');
});
});
it('honors ssr config', async () => {
const html = await fixture.readFile('/subpath/index.html');
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('#ssr-config').text()).to.equal('testing');
});

View file

@ -18,7 +18,7 @@ let config = await loadConfig({
cwd: fileURLToPath(projDir),
});
config.buildOptions.experimentalStaticBuild = true;
config.buildOptions.legacyBuild = false;
const server = await dev(config, { logging: { level: 'error' } });

View file

@ -156,10 +156,10 @@
jsonpointer "^5.0.0"
leven "^3.1.0"
"@astrojs/compiler@^0.11.4":
version "0.11.4"
resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.11.4.tgz#933853cf37ba2cbf0213a88463fd48c3a4329a07"
integrity sha512-T598FTCgBFjjPLPClvn+lc2SFGAJkjaF+lbxvHNjzmUpOYdz7YyH1apd3XAZvDp5r5WBBhicB6693GhQRpf3oQ==
"@astrojs/compiler@^0.12.0-next.5":
version "0.12.0-next.5"
resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.12.0-next.5.tgz#4e6d27c74787777522395018f2497dab4a032c77"
integrity sha512-4YVPRrB9JJhxoNC9PWN2zpGE7SXRAXcyCouawbd24iyBl4g9aRoQN12XA0qQZkbea9/NNLe9f2yhFMubM2CrJQ==
dependencies:
typescript "^4.3.5"
@ -1863,6 +1863,11 @@
dependencies:
"@types/unist" "*"
"@types/html-minifier-terser@^6.1.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==
"@types/is-ci@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-3.0.0.tgz#7e8910af6857601315592436f030aaa3ed9783c3"
@ -2896,6 +2901,14 @@ calmcard@~0.1.1:
resolved "https://registry.yarnpkg.com/calmcard/-/calmcard-0.1.1.tgz#35ac2b66492b0ed39ad06a893a0ff6e61124e449"
integrity sha1-NawrZkkrDtOa0GqJOg/25hEk5Ek=
camel-case@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
dependencies:
pascal-case "^3.1.2"
tslib "^2.0.3"
camelcase-css@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
@ -3089,6 +3102,13 @@ ci-info@^3.1.0, ci-info@^3.2.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2"
integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==
clean-css@^5.2.2:
version "5.2.4"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.2.4.tgz#982b058f8581adb2ae062520808fb2429bd487a4"
integrity sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==
dependencies:
source-map "~0.6.0"
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
@ -3208,6 +3228,11 @@ commander@^7.2.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
commander@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
commander@~6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
@ -3670,6 +3695,14 @@ domutils@^2.5.2, domutils@^2.7.0, domutils@^2.8.0:
domelementtype "^2.2.0"
domhandler "^4.2.0"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
dotenv@^8.1.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
@ -5013,6 +5046,19 @@ html-entities@2.3.2, html-entities@^2.3.2:
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488"
integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==
html-minifier-terser@^7.0.0-alpha.1:
version "7.0.0-alpha.1"
resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-7.0.0-alpha.1.tgz#0a5faf691277551e30656a8eee0af10d7efea229"
integrity sha512-tQwy+rdjixdS2oOf5l0A1VWHSRDPJcpmcUyGGIM1QybxswixhttRzRmqXR+2jLwl+iYoKndU1HBdVOftkFXmQw==
dependencies:
camel-case "^4.1.2"
clean-css "^5.2.2"
commander "^8.3.0"
entities "^3.0.1"
param-case "^3.0.4"
relateurl "^0.2.7"
terser "^5.10.0"
html-void-elements@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f"
@ -5834,6 +5880,13 @@ loupe@^2.3.1:
dependencies:
get-func-name "^2.0.0"
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
tslib "^2.0.3"
lru-cache@4.1.x, lru-cache@^4.0.1:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@ -6617,6 +6670,14 @@ nlcst-to-string@^3.0.0:
dependencies:
"@types/nlcst" "^1.0.0"
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
lower-case "^2.0.2"
tslib "^2.0.3"
node-abi@^3.3.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.8.0.tgz#679957dc8e7aa47b0a02589dbfde4f77b29ccb32"
@ -7002,6 +7063,14 @@ pac-resolver@^5.0.0:
ip "^1.1.5"
netmask "^2.0.1"
param-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@ -7074,6 +7143,14 @@ parse5@^6.0.0, parse5@^6.0.1:
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
pascal-case@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
@ -7707,6 +7784,11 @@ rehype-toc@^3.0.2:
dependencies:
"@jsdevtools/rehype-toc" "3.0.2"
relateurl@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
remark-autolink-headings@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/remark-autolink-headings/-/remark-autolink-headings-7.0.1.tgz#1d8528ea8783b828200d13c055ec2d5f2cae5060"
@ -8233,7 +8315,7 @@ source-map@^0.5.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@ -8700,7 +8782,7 @@ term-size@^2.1.0:
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
terser@^5.0.0:
terser@^5.0.0, terser@^5.10.0:
version "5.12.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.12.0.tgz#728c6bff05f7d1dcb687d8eace0644802a9dae8a"
integrity sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==
@ -8846,7 +8928,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.1, tslib@^2.2.0:
tslib@^2.0.1, tslib@^2.0.3, tslib@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==