0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

Fix VITE_ASSET bug (#3439)

* Fix VITE_ASSET bug

* Updated test that depended on esbuild output

* Fix some more tests

* Fix css config and postcss tests

* Git client only working

* Fix static build test

* Update tailwind tests

* Fix build

* Fix css bundling tests

* Updated some more tests for windows

* Remove tests that are no longer relevant

* Cause it to break

* Fix bug and add explanation

* Adds a changeset

* Inline comments about what the hashing is doing

* Update packages/astro/src/vite-plugin-build-css/index.ts

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>

* Update to the lockfile

* Minify css

* Update tailwind tests

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
This commit is contained in:
Matthew Phillips 2022-05-26 14:00:36 -04:00 committed by GitHub
parent 2f4ee560dd
commit ac3c60d48d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 336 additions and 420 deletions

View file

@ -0,0 +1,7 @@
---
'astro': patch
---
Fixes importing npm packages within CSS
This change fixes a longstanding bug where the string `VITE_ASSET` was left in CSS when trying to import CSS packages. The fix comes thanks to an upstream Vite feature that allows us to hand off most of the CSS bundling work to Vite.

View file

@ -77,7 +77,7 @@
"test:e2e:match": "playwright test -g"
},
"dependencies": {
"@astrojs/compiler": "^0.15.0",
"@astrojs/compiler": "^0.15.1",
"@astrojs/language-server": "^0.13.4",
"@astrojs/markdown-remark": "^0.10.1",
"@astrojs/prism": "0.4.1",

View file

@ -112,19 +112,17 @@ export function* getPageDatasByChunk(
}
}
export function* getPageDatasByClientOnlyChunk(
export function* getPageDatasByClientOnlyID(
internals: BuildInternals,
chunk: RenderedChunk
viteid: ViteID
): Generator<PageBuildData, void, unknown> {
const pagesByClientOnly = internals.pagesByClientOnly;
if (pagesByClientOnly.size) {
for (const [modulePath] of Object.entries(chunk.modules)) {
// prepend with `/@fs` to match the path used in the compiler's transform() call
const pathname = `/@fs${prependForwardSlash(modulePath)}`;
if (pagesByClientOnly.has(pathname)) {
for (const pageData of pagesByClientOnly.get(pathname)!) {
yield pageData;
}
const pathname = `/@fs${prependForwardSlash(viteid)}`;
const pageBuildDatas = pagesByClientOnly.get(pathname)
if(pageBuildDatas) {
for(const pageData of pageBuildDatas) {
yield pageData;
}
}
}

View file

@ -1,35 +1,12 @@
import { BuildInternals } from '../core/build/internal';
import type { ModuleInfo, PluginContext } from 'rollup';
import type { GetModuleInfo, ModuleInfo } from 'rollup';
import type { PageBuildData } from '../core/build/types';
import * as path from 'path';
import esbuild from 'esbuild';
import { Plugin as VitePlugin } from 'vite';
import { isCSSRequest } from '../core/render/util.js';
import {
getPageDatasByChunk,
getPageDataByViteID,
hasPageDataByViteID,
getPageDatasByClientOnlyChunk,
} from '../core/build/internal.js';
const PLUGIN_NAME = '@astrojs/rollup-plugin-build-css';
// This is a virtual module that represents the .astro <style> usage on a page
const ASTRO_STYLE_PREFIX = '@astro-inline-style';
const ASTRO_PAGE_STYLE_PREFIX = '@astro-page-all-styles';
function isStyleVirtualModule(id: string) {
return id.startsWith(ASTRO_STYLE_PREFIX);
}
function isPageStyleVirtualModule(id: string) {
return id.startsWith(ASTRO_PAGE_STYLE_PREFIX);
}
function isRawOrUrlModule(id: string) {
return id.match(/(\?|\&)([^=]+)(raw|url)/gm);
}
import { getPageDataByViteID, getPageDatasByClientOnlyID } from '../core/build/internal.js';
import { resolvedPagesVirtualModuleId } from '../core/app/index.js';
import crypto from 'crypto';
interface PluginOptions {
internals: BuildInternals;
@ -38,82 +15,84 @@ interface PluginOptions {
export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin {
const { internals } = options;
const styleSourceMap = new Map<string, string>();
function* walkStyles(
ctx: PluginContext,
id: string,
seen = new Set<string>()
): Generator<[string, string], void, unknown> {
// This walks up the dependency graph and yields out each ModuleInfo object.
function* walkParentInfos(id: string, ctx: {getModuleInfo: GetModuleInfo}, seen = new Set<string>()): Generator<ModuleInfo, void, unknown> {
seen.add(id);
if (styleSourceMap.has(id)) {
yield [id, styleSourceMap.get(id)!];
}
const info = ctx.getModuleInfo(id);
if (info) {
for (const importedId of [...info.importedIds, ...info.dynamicallyImportedIds]) {
if (!seen.has(importedId) && !isRawOrUrlModule(importedId)) {
yield* walkStyles(ctx, importedId, seen);
}
if(info) {
yield info;
}
const importers = (info?.importers || []).concat(info?.dynamicImporters || []);
for(const imp of importers) {
if(seen.has(imp)) {
continue;
}
yield * walkParentInfos(imp, ctx, seen);
}
}
// This function walks the dependency graph, going up until it finds a page component.
// This could be a .astro page or a .md page.
function* getTopLevelPages(id: string, ctx: {getModuleInfo: GetModuleInfo}): Generator<string, void, unknown> {
for(const info of walkParentInfos(id, ctx)) {
const importers = (info?.importers || []).concat(info?.dynamicImporters || []);
if(importers.length <= 2 && importers[0] === resolvedPagesVirtualModuleId) {
yield info.id;
}
}
}
/**
* This walks the dependency graph looking for styles that are imported
* by a page and then creates a chunking containing all of the styles for that page.
* Since there is only 1 entrypoint for the entire app, we do this in order
* to prevent adding all styles to all pages.
*/
async function addStyles(this: PluginContext) {
for (const id of this.getModuleIds()) {
if (hasPageDataByViteID(internals, id)) {
let pageStyles = '';
for (const [_styleId, styles] of walkStyles(this, id)) {
pageStyles += styles;
}
function createHashOfPageParents(id: string, ctx: {getModuleInfo: GetModuleInfo}): string {
const parents = Array.from(getTopLevelPages(id, ctx)).sort();
const hash = crypto.createHash('sha256');
for(const page of parents) {
hash.update(page, 'utf-8');
}
return hash.digest('hex').slice(0, 8);
}
// Pages with no styles, nothing more to do
if (!pageStyles) continue;
const { code: minifiedCSS } = await esbuild.transform(pageStyles, {
loader: 'css',
minify: true,
});
const referenceId = this.emitFile({
name: 'entry' + '.css',
type: 'asset',
source: minifiedCSS,
});
const fileName = this.getFileName(referenceId);
// Add CSS file to the page's pageData, so that it will be rendered with
// the correct links.
getPageDataByViteID(internals, id)?.css.add(fileName);
}
function* getParentClientOnlys(id: string, ctx: {getModuleInfo: GetModuleInfo}): Generator<PageBuildData, void, unknown> {
for(const info of walkParentInfos(id, ctx)) {
yield * getPageDatasByClientOnlyID(internals, info.id);
}
}
return {
name: PLUGIN_NAME,
name: '@astrojs/rollup-plugin-build-css',
configResolved(resolvedConfig) {
// Our plugin needs to run before `vite:css-post` which does a lot of what we do
// for bundling CSS, but since we need to control CSS we should go first.
// We move to right before the vite:css-post plugin so that things like the
// Vue plugin go before us.
// Our plugin needs to run before `vite:css-post` because we have to modify
// The bundles before vite:css-post sees them. We can remove this code
// after this bug is fixed: https://github.com/vitejs/vite/issues/8330
const plugins = resolvedConfig.plugins as VitePlugin[];
const viteCSSPostIndex = resolvedConfig.plugins.findIndex((p) => p.name === 'vite:css-post');
if (viteCSSPostIndex !== -1) {
const viteCSSPost = plugins[viteCSSPostIndex];
// Prevent this plugin's bundling behavior from running since we need to
// do that ourselves in order to handle updating the HTML.
delete viteCSSPost.renderChunk;
delete viteCSSPost.generateBundle;
// Wrap the renderChunk hook in CSSPost to enable minification.
// We do this instead of setting minification globally to avoid minifying
// server JS.
const renderChunk = viteCSSPost.renderChunk;
if(renderChunk) {
viteCSSPost.renderChunk = async function(...args) {
const minifyOption = resolvedConfig.build.minify;
if(minifyOption === false) {
resolvedConfig.build.minify = 'esbuild';
}
const result = await renderChunk.apply(this, args);
if(typeof result === 'string') {
return {
code: result
};
}
resolvedConfig.build.minify = minifyOption;
return result || null;
};
}
// Move our plugin to be right before this one.
const ourIndex = plugins.findIndex((p) => p.name === PLUGIN_NAME);
const ourIndex = plugins.findIndex((p) => p.name === '@astrojs/rollup-plugin-build-css');
const ourPlugin = plugins[ourIndex];
// Remove us from where we are now and place us right before the viteCSSPost plugin
@ -121,110 +100,80 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin {
plugins.splice(viteCSSPostIndex - 1, 0, ourPlugin);
}
},
async resolveId(id) {
if (isPageStyleVirtualModule(id)) {
return id;
}
if (isStyleVirtualModule(id)) {
return id;
}
return undefined;
outputOptions(outputOptions) {
const manualChunks = outputOptions.manualChunks || Function.prototype;
outputOptions.manualChunks = function(id, ...args) {
// Defer to user-provided `manualChunks`, if it was provided.
if(typeof manualChunks == 'object') {
if(id in manualChunks) {
return manualChunks[id];
}
} else if(typeof manualChunks === 'function') {
const outid = manualChunks.call(this, id, ...args);
if(outid) {
return outid;
}
}
// For CSS, create a hash of all of the pages that use it.
// This causes CSS to be built into shared chunks when used by multiple pages.
if (isCSSRequest(id)) {
return createHashOfPageParents(id, args[0]);
}
};
},
async transform(value, id) {
if (isStyleVirtualModule(id)) {
styleSourceMap.set(id, value);
}
if (isCSSRequest(id)) {
styleSourceMap.set(id, value);
}
return null;
},
async renderChunk(_code, chunk) {
if (options.target === 'server') return null;
let chunkCSS = '';
let isPureCSS = true;
for (const [id] of Object.entries(chunk.modules)) {
if (!isCSSRequest(id) && !isPageStyleVirtualModule(id)) {
isPureCSS = false;
}
if (styleSourceMap.has(id)) {
chunkCSS += styleSourceMap.get(id)!;
}
async generateBundle(_outputOptions, bundle) {
type ViteMetadata = {
importedAssets: Set<string>;
importedCss: Set<string>;
}
if (!chunkCSS) return null; // dont output empty .css files
for (const [_, chunk] of Object.entries(bundle)) {
if(chunk.type === 'chunk') {
const c = chunk;
if('viteMetadata' in chunk) {
const meta = chunk['viteMetadata'] as ViteMetadata;
if (isPureCSS) {
internals.pureCSSChunks.add(chunk);
}
// Chunks that have the viteMetadata.importedCss are CSS chunks
if(meta.importedCss.size) {
// For the client build, client:only styles need to be mapped
// over to their page. For this chunk, determine if it's a child of a
// client:only component and if so, add its CSS to the page it belongs to.
if(options.target === 'client') {
for(const [id] of Object.entries(c.modules)) {
for(const pageData of getParentClientOnlys(id, this)) {
for(const importedCssImport of meta.importedCss) {
pageData.css.add(importedCssImport);
}
}
}
}
const { code: minifiedCSS } = await esbuild.transform(chunkCSS, {
loader: 'css',
minify: true,
});
const referenceId = this.emitFile({
name: chunk.name + '.css',
type: 'asset',
source: minifiedCSS,
});
if (chunk.type === 'chunk') {
const fileName = this.getFileName(referenceId);
for (const pageData of getPageDatasByChunk(internals, chunk)) {
pageData.css.add(fileName);
}
// Adds this CSS for client:only components to the appropriate page
for (const pageData of getPageDatasByClientOnlyChunk(internals, chunk)) {
pageData.css.add(fileName);
}
}
return null;
},
// Delete CSS chunks so JS is not produced for them.
async generateBundle(opts, bundle) {
const hasPureCSSChunks = internals.pureCSSChunks.size;
const pureChunkFilenames = new Set(
[...internals.pureCSSChunks].map((chunk) => chunk.fileName)
);
const emptyChunkFiles = [...pureChunkFilenames]
.map((file) => path.basename(file))
.join('|')
.replace(/\./g, '\\.');
const emptyChunkRE = new RegExp(
opts.format === 'es'
? `\\bimport\\s*"[^"]*(?:${emptyChunkFiles})";\n?`
: `\\brequire\\(\\s*"[^"]*(?:${emptyChunkFiles})"\\);\n?`,
'g'
);
// Crawl the module graph to find CSS chunks to create
await addStyles.call(this);
for (const [chunkId, chunk] of Object.entries(bundle)) {
if (chunk.type === 'chunk') {
// Removes imports for pure CSS chunks.
if (hasPureCSSChunks) {
if (internals.pureCSSChunks.has(chunk) && !chunk.exports.length) {
// Delete pure CSS chunks, these are JavaScript chunks that only import
// other CSS files, so are empty at the end of bundling.
delete bundle[chunkId];
} else {
// Remove any pure css chunk imports from JavaScript.
// Note that this code comes from Vite's CSS build plugin.
chunk.code = chunk.code.replace(
emptyChunkRE,
// remove css import while preserving source map location
(m) => `/* empty css ${''.padEnd(m.length - 15)}*/`
);
// For this CSS chunk, walk parents until you find a page. Add the CSS to that page.
for(const [id] of Object.entries(c.modules)) {
for(const pageViteID of getTopLevelPages(id, this)) {
const pageData = getPageDataByViteID(internals, pageViteID);
for(const importedCssImport of meta.importedCss) {
pageData?.css.add(importedCssImport);
}
}
}
}
}
}
if (chunk.type === 'chunk') {
// This simply replaces single quotes with double quotes because the vite:css-post
// plugin only works with single for some reason. This code can be removed
// When the Vite bug is fixed: https://github.com/vitejs/vite/issues/8330
const exp = new RegExp(`(\\bimport\\s*)[']([^']*(?:[a-z]+\.[0-9a-z]+\.m?js))['](;\n?)`, 'g');
chunk.code = chunk.code.replace(exp, (_match, begin, chunkPath, end) => {
return begin + '"' + chunkPath + '"' + end;
});
}
}
},
}
};
}

View file

@ -28,7 +28,8 @@ describe('CSS', function () {
const html = await fixture.readFile('/index.html');
$ = cheerio.load(html);
const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href');
bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')))
.replace(/\s/g, '').replace('/n', '');
});
describe('Astro Styles', () => {
@ -102,7 +103,7 @@ describe('CSS', function () {
expect(el.attr('class')).to.include(moduleClass);
// 2. check CSS
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy`));
});
it('.sass', async () => {
@ -112,7 +113,7 @@ describe('CSS', function () {
expect(el.attr('class')).to.include('react-sass-title');
// 2. check CSS
expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy}`));
expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy`));
});
it('.scss', async () => {
@ -122,7 +123,7 @@ describe('CSS', function () {
expect(el.attr('class')).to.include('react-scss-title');
// 2. check CSS
expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy}`));
expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy`));
});
it('.module.sass', async () => {
@ -134,7 +135,7 @@ describe('CSS', function () {
expect(el.attr('class')).to.include(moduleClass);
// 2. check CSS
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy`));
});
it('.module.scss', async () => {
@ -146,7 +147,7 @@ describe('CSS', function () {
expect(el.attr('class')).to.include(moduleClass);
// 2. check CSS
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`));
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy`));
});
});
@ -185,7 +186,7 @@ describe('CSS', function () {
expect(el.attr('class')).to.include(moduleClass);
// 2. check CSS
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:cursive}`));
expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:cursive`));
});
it('<style lang="sass">', async () => {
@ -222,7 +223,7 @@ describe('CSS', function () {
// 2. check CSS
expect(bundledCSS).to.match(
new RegExp(`.svelte-css.${scopedClass}[^{]*{font-family:Comic Sans MS`)
new RegExp(`.svelte-css.${scopedClass}[^{]*{font-family:ComicSansMS`)
);
});
@ -238,7 +239,7 @@ describe('CSS', function () {
// 2. check CSS
expect(bundledCSS).to.match(
new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:Comic Sans MS`)
new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:ComicSansMS`)
);
});
@ -254,7 +255,7 @@ describe('CSS', function () {
// 2. check CSS
expect(bundledCSS).to.match(
new RegExp(`.svelte-scss.${scopedClass}[^{]*{font-family:Comic Sans MS`)
new RegExp(`.svelte-scss.${scopedClass}[^{]*{font-family:ComicSansMS`)
);
});
});

View file

@ -28,7 +28,12 @@ describe('Client only components', () => {
it('Adds the CSS to the page', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerioLoad(html);
expect($('link[rel=stylesheet]')).to.have.lengthOf(2);
const href = $('link[rel=stylesheet]').attr('href');
const css = await fixture.readFile(href);
expect(css).to.match(/yellowgreen/, 'Svelte styles are added');
expect(css).to.match(/Courier New/, 'Global styles are added');
});
});
@ -60,6 +65,11 @@ describe('Client only components subpath', () => {
it('Adds the CSS to the page', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerioLoad(html);
expect($('link[rel=stylesheet]')).to.have.lengthOf(2);
const href = $('link[rel=stylesheet]').attr('href');
const css = await fixture.readFile(href.replace(/\/blog/, ''));
expect(css).to.match(/yellowgreen/, 'Svelte styles are added');
expect(css).to.match(/Courier New/, 'Global styles are added');
});
});

View file

@ -1,79 +0,0 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('CSS Bundling (ESM import)', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-css-bundling-import/',
});
await fixture.build();
});
it('CSS output in import order', async () => {
// note: this test is a little confusing, but the main idea is that
// page-2.astro contains all of page-1.astro, plus some unique styles.
// we only test page-2 to ensure the proper order is observed.
const html = await fixture.readFile('/page-2/index.html');
const $ = cheerio.load(html);
let css = '';
for (const style of $('link[rel=stylesheet]')) {
const href = style.attribs.href.replace(/^\.\./, '');
if (!href) continue;
css += await fixture.readFile(href);
}
// test 1: insure green comes after red (site.css)
expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:red}'));
// test 2: insure green comes after blue (page-1.css)
expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:#00f}'));
});
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);
for (const style of $('link[rel=stylesheet]')) {
const href = style.attribs.href.replace(/^\.\./, '');
if (!href) continue;
const css = await fixture.readFile(href);
expect(css).to.be.ok;
}
}
});
it('?raw and ?url CSS imports are ignored', async () => {
// note: this test is a little confusing as well, but the main idea is that
// page-3.astro should have site.css imported as an ESM in InlineLayout.astro
// as well as the styles from page-3.css as an inline <style>.
const html = await fixture.readFile('/page-3/index.html');
const $ = cheerio.load(html);
let css = '';
for (const style of $('link[rel=stylesheet]')) {
const href = style.attribs.href.replace(/^\.\./, '');
if (!href) continue;
css += await fixture.readFile(href);
}
// test 1: insure green is included (site.css)
expect(css.indexOf('p{color:red}')).to.be.greaterThanOrEqual(0);
// test 2: insure purple is not included as an import (page-3.css)
// this makes sure the styles imported with ?raw and ?url weren't bundled
expect(css.indexOf('p{color:purple}')).to.be.lessThan(0);
// test 3: insure purple was inlined (page-3.css inlined with set:html)
// this makes sure the styles imported with ?url were inlined
let inlineCss = $('style').html().replace(/\s/g, '').replace('/n', '');
expect(inlineCss.indexOf('p{color:purple;}')).to.be.greaterThanOrEqual(0);
});
});

View file

@ -13,6 +13,6 @@ describe('Vite Config', async () => {
it('Allows overriding bundle naming options in the build', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('link').attr('href')).to.equal('/assets/testing-entry.css');
expect($('link').attr('href')).to.match(/\/assets\/testing-[a-z0-9]+\.css/)
});
});

View file

@ -0,0 +1,49 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Assets in CSS', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/css-assets/',
vite: {
build: {
assetsInlineLimit: 0
}
}
});
await fixture.build();
});
function getAllMatches(re, text) {
let count = 0;
while (re.exec(text) !== null) {
++count;
}
return count;
}
async function getCSSForPage(pathname) {
const html = await fixture.readFile(pathname);
const $ = cheerio.load(html);
const cssPath = $('link').attr('href');
const css = await fixture.readFile(cssPath);
return css;
}
it('Bundled CSS does not have __VITE_ASSET__', async () => {
let css = await getCSSForPage('/one/index.html');
expect(css).to.not.contain('__VITE_ASSET__');
css = await getCSSForPage('/two/index.html');
expect(css).to.not.contain('__VITE_ASSET__');
});
it('Pages contain only their own CSS', async () => {
let css = await getCSSForPage('/one/index.html');
expect(getAllMatches(/font-face/g, css)).to.equal(1);
css = await getCSSForPage('/two/index.html');
expect(getAllMatches(/font-face/g, css)).to.equal(1);
});
});

View file

@ -1,8 +0,0 @@
{
"name": "@test/astro-css-bundling-import",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*"
}
}

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-1">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,28 +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>
</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-3">Page 3</a></li>
<!-- <li><a href="/page-2-reduced-layout">Page 2 reduced layout</a></li> -->
</ul>
<main id="page">
<slot></slot>
</main>
</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,8 +0,0 @@
---
import PageLayout from "../layouts/PageLayout.astro"
---
<PageLayout title="Page 1">
<h1>Page 1</h1>
<p>Nothing to see here. Check <a href="/page-2">Page 2</a></p>
</PageLayout>

View file

@ -1,13 +0,0 @@
---
import PageLayout from "../layouts/PageLayout.astro"
import "../styles/page-two.css"
---
<PageLayout title="Page 2">
<h1>Page 2</h1>
<p>This text should be green, because we want <code>page-2.css</code> to override <code>site.css</code></p>
<p>This works in the dev-server. However in the prod build, the text is blue. Execute <code>npm run build</code> and then execute <code>npx http-server dist/</code>.</p>
<p>We can view the built html at <a href="https://github-qoihup--8080.local.webcontainer.io/page-2/">https://github-qoihup--8080.local.webcontainer.io/page-2/</a>. The color there is blue.</p>
<p>If it helps debug the issue, rename the <code>page-1.astro</code> file to <code>page-1.astro.bak</code>. Build the prod site and view it. This time the color is green.</p>
</PageLayout>

View file

@ -1,12 +0,0 @@
---
import PageLayout from "../layouts/InlineLayout.astro"
import styles from "../styles/page-three.css?raw"
import stylesUrl from "../styles/page-one.css?url"
---
<PageLayout title="Page 3">
<style set:html={styles}></style>
<h1>Page 3</h1>
<p>This text should be purple, because we want the inlined <code>page-3.css</code> to override <code>site.css</code></p>
</PageLayout>

View file

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

View file

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

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

@ -0,0 +1,9 @@
{
"name": "@test/astro-sitemap-rss",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/test-font-awesome-package": "file:./packages/font-awesome"
}
}

View file

@ -0,0 +1,19 @@
HTTP/2 200
date: Tue, 24 May 2022 14:15:13 GMT
content-type: font/woff2
content-length: 77160
access-control-allow-origin: *
cache-control: public, max-age=31536000
last-modified: Mon, 24 Oct 2016 21:33:21 GMT
etag: "12d68-1vSMun0Hb7by/Wupk6dbncHsvww"
via: 1.1 fly.io
fly-request-id: 01FDSA73RWTQANDR54ZMWPKC4B
cf-cache-status: HIT
age: 23685787
accept-ranges: bytes
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
strict-transport-security: max-age=31536000; includeSubDomains; preload
x-content-type-options: nosniff
server: cloudflare
cf-ray: 7106a46819b78005-IAD

View file

@ -0,0 +1,19 @@
HTTP/2 200
date: Tue, 24 May 2022 14:15:13 GMT2
content-type: font/woff2
content-length: 77160
access-control-allow-origin: *
cache-control: public, max-age=31536000
last-modified: Mon, 24 Oct 2016 21:33:21 GMT
etag: "12d68-1vSMun0Hb7by/Wupk6dbncHsvww"
via: 1.1 fly.io
fly-request-id: 01FDSA73RWTQANDR54ZMWPKC4B
cf-cache-status: HIT
age: 23685787
accept-ranges: bytes
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
strict-transport-security: max-age=31536000; includeSubDomains; preload
x-content-type-options: nosniff
server: cloudflare
cf-ray: 7106a46819b78005-IAD

View file

@ -0,0 +1,4 @@
{
"name": "@astrojs/test-font-awesome-package",
"version": "0.0.1"
}

View file

@ -0,0 +1,20 @@
<html>
<head><title>Test</title>
<style>
@font-face {
font-family: 'FA';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('@astrojs/test-font-awesome-package/fontawesome-webfont.woff2')
format('woff2');
}
body {
font-family: 'FA';
}
</style>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,22 @@
<html>
<head>
<title>Test2</title>
<style>
@font-face {
font-family: 'FA2';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('@astrojs/test-font-awesome-package/fontawesome-webfont2.woff2')
format('woff2');
}
body {
font-family: 'FA2';
}
</style>
</head>
<body>
<h1>test2</h1>
</body>
</html>

View file

@ -4,7 +4,7 @@ import eol from 'eol';
import { loadFixture } from './test-utils.js';
describe('PostCSS', () => {
const PREFIXED_CSS = `{-webkit-appearance:none;appearance:none}`;
const PREFIXED_CSS = `{-webkit-appearance:none;appearance:none`;
let fixture;
let bundledCSS;
@ -18,7 +18,8 @@ describe('PostCSS', () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href');
bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')))
.replace(/\s/g, '').replace('/n', '');
});
it('works in Astro page styles', () => {
@ -42,8 +43,8 @@ describe('PostCSS', () => {
});
it('ignores CSS in public/', async () => {
const publicCSS = await fixture.readFile('/global.css');
const publicCSS = (await fixture.readFile('/global.css')).trim().replace(/\s/g, '').replace('/n', '');
// neither minified nor prefixed
expect(eol.lf(publicCSS.trim())).to.equal(`.global {\n appearance: none;\n}`);
expect(eol.lf(publicCSS)).to.equal(`.global{appearance:none;}`);
});
});

View file

@ -39,7 +39,7 @@ describe('Tailwind', () => {
// tailwind escapes brackets, `font-[900]` compiles to `font-\[900\]`
expect(bundledCSS, 'supports arbitrary value classes').to.match(
/\.font-\\\[900\\\]{font-weight:900}/
/\.font-\\\[900\\\]{/
);
// custom theme colors were included

View file

@ -459,7 +459,7 @@ importers:
packages/astro:
specifiers:
'@astrojs/compiler': ^0.15.0
'@astrojs/compiler': ^0.15.1
'@astrojs/language-server': ^0.13.4
'@astrojs/markdown-remark': ^0.10.1
'@astrojs/prism': 0.4.1
@ -545,7 +545,7 @@ importers:
yargs-parser: ^21.0.1
zod: ^3.17.3
dependencies:
'@astrojs/compiler': 0.15.0
'@astrojs/compiler': 0.15.1
'@astrojs/language-server': 0.13.4
'@astrojs/markdown-remark': link:../markdown/remark
'@astrojs/prism': link:../astro-prism
@ -873,12 +873,6 @@ importers:
dependencies:
astro: link:../../..
packages/astro/test/fixtures/astro-css-bundling-import:
specifiers:
astro: workspace:*
dependencies:
astro: link:../../..
packages/astro/test/fixtures/astro-css-bundling-nested-layouts:
specifiers:
astro: workspace:*
@ -1117,6 +1111,17 @@ importers:
dependencies:
astro: link:../../..
packages/astro/test/fixtures/css-assets:
specifiers:
'@astrojs/test-font-awesome-package': file:./packages/font-awesome
astro: workspace:*
dependencies:
'@astrojs/test-font-awesome-package': file:packages/astro/test/fixtures/css-assets/packages/font-awesome
astro: link:../../..
packages/astro/test/fixtures/css-assets/packages/font-awesome:
specifiers: {}
packages/astro/test/fixtures/custom-404:
specifiers:
astro: workspace:*
@ -2059,8 +2064,8 @@ packages:
leven: 3.1.0
dev: true
/@astrojs/compiler/0.15.0:
resolution: {integrity: sha512-gsw9OP/mfMIrdbrBaG5BpP98sWiAymeeVJTi365OwzDvOaePUzKqAMGCQd07RAfeX9eQzTkjDeJJZkIoyt575w==}
/@astrojs/compiler/0.15.1:
resolution: {integrity: sha512-4PcWD7lfX1c6J9GwaVVbQjrMQEtqx3jXRGj92TCIc0EQ6kRKxLymdzRS9eW8yCsgJp6Ym4nCzQ8ifTSVSphjDQ==}
dependencies:
tsm: 2.2.1
uvu: 0.5.3
@ -13807,3 +13812,9 @@ packages:
/zwitch/2.0.2:
resolution: {integrity: sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==}
file:packages/astro/test/fixtures/css-assets/packages/font-awesome:
resolution: {directory: packages/astro/test/fixtures/css-assets/packages/font-awesome, type: directory}
name: '@astrojs/test-font-awesome-package'
version: 0.0.1
dev: false