Add CSS injection, fix portfolio example (#1648)
* Fix portfolio example * Add .pcss extension * Update load ssr opts * Update packages/astro/src/runtime/server/index.ts Co-authored-by: Jonathan Neal <jonathantneal@hotmail.com> Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com> Co-authored-by: Jonathan Neal <jonathantneal@hotmail.com>
This commit is contained in:
parent
1103cb1f9d
commit
07f786096d
14 changed files with 203 additions and 93 deletions
|
@ -190,28 +190,19 @@ const featuredProject = projects[0];
|
||||||
height="1131"
|
height="1131"
|
||||||
class="img"
|
class="img"
|
||||||
src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
|
src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
|
||||||
srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,
|
srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w"
|
||||||
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,
|
|
||||||
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,
|
|
||||||
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w,"
|
|
||||||
sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
|
sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
|
||||||
/>
|
>
|
||||||
<div class="gradient" />
|
<div class="gradient"></div>
|
||||||
<div class="gradient2" />
|
<div class="gradient2"></div>
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
<h1 class="title">
|
<h1 class="title">
|
||||||
<small class="subtitle">The personal site of </small>Jeanine White
|
<small class="subtitle">The personal site of </small>Jeanine White
|
||||||
</h1>
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
<span class="role">
|
<span class="role">👩💻 Developer <span class="invert">👩💻 Developer</span></span>
|
||||||
👩💻 Developer <span class="invert">👩💻 Developer</span>
|
<span class="role">🎤 Speaker <span class="invert">🎤 Speaker</span></span>
|
||||||
</span>
|
<span class="role">✏️ Writer <span class="invert">✏️ Writer</span></span>
|
||||||
<span class="role">
|
|
||||||
🎤 Speaker <span class="invert">🎤 Speaker</span>
|
|
||||||
</span>
|
|
||||||
<span class="role">
|
|
||||||
✏️ Writer <span class="invert">✏️ Writer</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="desc">Lover of dogs, roadtrips, and poetry.</p>
|
<p class="desc">Lover of dogs, roadtrips, and poetry.</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -233,8 +224,7 @@ const featuredProject = projects[0];
|
||||||
<span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
|
<span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
|
||||||
<a href="https://github.com/snowpackjs/astro" target="_blank" rel="nofollow">
|
<a href="https://github.com/snowpackjs/astro" target="_blank" rel="nofollow">
|
||||||
Astro
|
Astro
|
||||||
</a>
|
</a>, a new way to build static sites. This is just an example template for you to modify.
|
||||||
, a new way to build static sites. This is just an example template for you to modify.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="/about">Read more</a>
|
<a href="/about">Read more</a>
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
"author": "Skypack",
|
"author": "Skypack",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "./dist/types/@types/astro-public.d.ts",
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/snowpackjs/astro.git",
|
"url": "https://github.com/snowpackjs/astro.git",
|
||||||
|
@ -53,7 +52,7 @@
|
||||||
"test": "mocha --parallel --timeout 15000"
|
"test": "mocha --parallel --timeout 15000"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/compiler": "^0.2.16",
|
"@astrojs/compiler": "^0.2.17",
|
||||||
"@astrojs/language-server": "^0.7.16",
|
"@astrojs/language-server": "^0.7.16",
|
||||||
"@astrojs/markdown-remark": "^0.3.1",
|
"@astrojs/markdown-remark": "^0.3.1",
|
||||||
"@astrojs/markdown-support": "0.3.1",
|
"@astrojs/markdown-support": "0.3.1",
|
||||||
|
@ -73,13 +72,14 @@
|
||||||
"estree-util-value-to-estree": "^1.2.0",
|
"estree-util-value-to-estree": "^1.2.0",
|
||||||
"fast-xml-parser": "^3.19.0",
|
"fast-xml-parser": "^3.19.0",
|
||||||
"html-entities": "^2.3.2",
|
"html-entities": "^2.3.2",
|
||||||
|
"htmlparser2": "^7.1.2",
|
||||||
"kleur": "^4.1.4",
|
"kleur": "^4.1.4",
|
||||||
"mime": "^2.5.2",
|
"mime": "^2.5.2",
|
||||||
"morphdom": "^2.6.1",
|
"morphdom": "^2.6.1",
|
||||||
"node-fetch": "^2.6.5",
|
"node-fetch": "^2.6.5",
|
||||||
"path-to-regexp": "^6.2.0",
|
"path-to-regexp": "^6.2.0",
|
||||||
"remark-slug": "^7.0.0",
|
"remark-slug": "^7.0.0",
|
||||||
"sass": "^1.43.2",
|
"sass": "^1.43.3",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"send": "^0.17.1",
|
"send": "^0.17.1",
|
||||||
"shiki": "^0.9.10",
|
"shiki": "^0.9.10",
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export { AstroConfig, AstroUserConfig } from './astro-core';
|
|
64
packages/astro/src/core/ssr/css.ts
Normal file
64
packages/astro/src/core/ssr/css.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import type vite from '../../../vendor/vite';
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import htmlparser2 from 'htmlparser2';
|
||||||
|
|
||||||
|
// https://vitejs.dev/guide/features.html#css-pre-processors
|
||||||
|
export const STYLE_EXTENSIONS = new Set(['.css', '.pcss', '.scss', '.sass', '.styl', '.stylus', '.less']);
|
||||||
|
export const PREPROCESSOR_EXTENSIONS = new Set(['.pcss', '.scss', '.sass', '.styl', '.stylus', '.less']);
|
||||||
|
|
||||||
|
/** find unloaded styles */
|
||||||
|
export function getStylesForID(id: string, viteServer: vite.ViteDevServer): Set<string> {
|
||||||
|
const css = new Set<string>();
|
||||||
|
const { idToModuleMap } = viteServer.moduleGraph;
|
||||||
|
const moduleGraph = idToModuleMap.get(id);
|
||||||
|
if (!moduleGraph) return css;
|
||||||
|
|
||||||
|
// recursively crawl module graph to get all style files imported by parent id
|
||||||
|
function crawlCSS(entryModule: string, scanned = new Set<string>()) {
|
||||||
|
const moduleName = idToModuleMap.get(entryModule);
|
||||||
|
if (!moduleName) return;
|
||||||
|
for (const importedModule of moduleName.importedModules) {
|
||||||
|
if (!importedModule.id || scanned.has(importedModule.id)) return;
|
||||||
|
const ext = path.extname(importedModule.id.toLowerCase());
|
||||||
|
|
||||||
|
if (STYLE_EXTENSIONS.has(ext)) {
|
||||||
|
css.add(importedModule.id); // if style file, add to list
|
||||||
|
} else {
|
||||||
|
crawlCSS(importedModule.id, scanned); // otherwise, crawl file to see if it imports any CSS
|
||||||
|
}
|
||||||
|
scanned.add(importedModule.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crawlCSS(id);
|
||||||
|
|
||||||
|
return css;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** add CSS <link> tags to HTML */
|
||||||
|
export function addLinkTagsToHTML(html: string, styles: Set<string>): string {
|
||||||
|
let output = html;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// get position of </head>
|
||||||
|
let headEndPos = -1;
|
||||||
|
const parser = new htmlparser2.Parser({
|
||||||
|
onclosetag(tagname) {
|
||||||
|
if (tagname === 'head') {
|
||||||
|
headEndPos = parser.startIndex;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
parser.write(html);
|
||||||
|
parser.end();
|
||||||
|
|
||||||
|
// update html
|
||||||
|
if (headEndPos !== -1) {
|
||||||
|
output = html.substring(0, headEndPos) + [...styles].map((href) => `<link rel="stylesheet" type="text/css" href="${href}">`).join('') + html.substring(headEndPos);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// on invalid HTML, do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { renderPage, renderSlot } from '../../runtime/server/index.js';
|
import { renderPage, renderSlot } from '../../runtime/server/index.js';
|
||||||
import { canonicalURL as getCanonicalURL, codeFrame } from '../util.js';
|
import { canonicalURL as getCanonicalURL, codeFrame } from '../util.js';
|
||||||
|
import { addLinkTagsToHTML, getStylesForID } from './css.js';
|
||||||
import { generatePaginateFunction } from './paginate.js';
|
import { generatePaginateFunction } from './paginate.js';
|
||||||
import { getParams, validateGetStaticPathsModule, validateGetStaticPathsResult } from './routing.js';
|
import { getParams, validateGetStaticPathsModule, validateGetStaticPathsResult } from './routing.js';
|
||||||
|
|
||||||
|
@ -148,18 +149,25 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna
|
||||||
({ default: render } = await import(render));
|
({ default: render } = await import(render));
|
||||||
}
|
}
|
||||||
const { code } = await render(content, { ...renderOpts, ...(opts ?? {}) });
|
const { code } = await render(content, { ...renderOpts, ...(opts ?? {}) });
|
||||||
return code
|
return code;
|
||||||
}
|
},
|
||||||
} as unknown as AstroGlobal;
|
} as unknown as AstroGlobal;
|
||||||
},
|
},
|
||||||
_metadata: { renderers },
|
_metadata: { renderers },
|
||||||
};
|
};
|
||||||
|
|
||||||
let html = await renderPage(result, Component, pageProps, null);
|
let html = await renderPage(result, Component, pageProps, null);
|
||||||
|
|
||||||
// run transformIndexHtml() in development to add HMR client to the page.
|
// run transformIndexHtml() in development to add HMR client to the page.
|
||||||
if (mode === 'development') {
|
if (mode === 'development') {
|
||||||
html = await viteServer.transformIndexHtml(fileURLToPath(filePath), html, pathname);
|
html = await viteServer.transformIndexHtml(fileURLToPath(filePath), html, pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert CSS imported from Astro and JS components
|
||||||
|
const styles = getStylesForID(fileURLToPath(filePath), viteServer);
|
||||||
|
const relativeStyles = new Set<string>([...styles].map((url) => url.replace(fileURLToPath(astroConfig.projectRoot), '/')));
|
||||||
|
html = addLinkTagsToHTML(html, relativeStyles);
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
viteServer.ssrFixStacktrace(e);
|
viteServer.ssrFixStacktrace(e);
|
||||||
|
|
|
@ -132,7 +132,7 @@ function extractDirectives(inputProps: Record<string | number, any>): ExtractedP
|
||||||
}
|
}
|
||||||
} else if (key === 'class:list') {
|
} else if (key === 'class:list') {
|
||||||
// support "class" from an expression passed into a component (#782)
|
// support "class" from an expression passed into a component (#782)
|
||||||
extracted.props[key.slice(0, -5)] = serializeListValue(value)
|
extracted.props[key.slice(0, -5)] = serializeListValue(value);
|
||||||
} else {
|
} else {
|
||||||
extracted.props[key] = value;
|
extracted.props[key] = value;
|
||||||
}
|
}
|
||||||
|
@ -308,29 +308,32 @@ export function spreadAttributes(values: Record<any, any>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeListValue(value: any) {
|
function serializeListValue(value: any) {
|
||||||
const hash: Record<string, any> = {}
|
const hash: Record<string, any> = {};
|
||||||
|
|
||||||
push(value)
|
push(value);
|
||||||
|
|
||||||
return Object.keys(hash).join(' ');
|
return Object.keys(hash).join(' ');
|
||||||
|
|
||||||
function push(item: any) {
|
function push(item: any) {
|
||||||
// push individual iteratables
|
// push individual iteratables
|
||||||
if (item && typeof item.forEach === 'function') item.forEach(push)
|
if (item && typeof item.forEach === 'function') item.forEach(push);
|
||||||
|
|
||||||
// otherwise, push object value keys by truthiness
|
// otherwise, push object value keys by truthiness
|
||||||
else if (item === Object(item)) Object.keys(item).forEach(
|
else if (item === Object(item))
|
||||||
name => {
|
Object.keys(item).forEach((name) => {
|
||||||
if (item[name]) push(name)
|
if (item[name]) push(name);
|
||||||
}
|
});
|
||||||
)
|
|
||||||
|
|
||||||
// otherwise, push any other values as a string
|
// otherwise, push any other values as a string
|
||||||
else if (item = item != null && String(item).trim()) item.split(/\s+/).forEach(
|
else {
|
||||||
(name: string) => {
|
// get the item as a string
|
||||||
hash[name] = true
|
item = item == null ? '' : String(item).trim();
|
||||||
|
|
||||||
|
// add the item if it is filled
|
||||||
|
if (item) {
|
||||||
|
item.split(/\s+/).forEach((name: string) => {
|
||||||
|
hash[name] = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { TransformResult } from '@astrojs/compiler';
|
import type { TransformResult } from '@astrojs/compiler';
|
||||||
|
import type { SourceMapInput } from 'rollup';
|
||||||
import type vite from '../core/vite';
|
import type vite from '../core/vite';
|
||||||
import type { AstroConfig } from '../@types/astro-core';
|
import type { AstroConfig } from '../@types/astro-core';
|
||||||
|
|
||||||
|
@ -15,6 +16,20 @@ interface AstroPluginOptions {
|
||||||
devServer?: AstroDevServer;
|
devServer?: AstroDevServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/vitejs/vite/discussions/5109#discussioncomment-1450726
|
||||||
|
function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
|
||||||
|
if (options === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeof options === 'boolean') {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
if (typeof options == 'object') {
|
||||||
|
return !!options.ssr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Transform .astro files for Vite */
|
/** Transform .astro files for Vite */
|
||||||
export default function astro({ config, devServer }: AstroPluginOptions): vite.Plugin {
|
export default function astro({ config, devServer }: AstroPluginOptions): vite.Plugin {
|
||||||
let viteTransform: TransformHook;
|
let viteTransform: TransformHook;
|
||||||
|
@ -25,7 +40,7 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
|
||||||
viteTransform = getViteTransform(resolvedConfig);
|
viteTransform = getViteTransform(resolvedConfig);
|
||||||
},
|
},
|
||||||
// note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.)
|
// note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.)
|
||||||
async load(id) {
|
async load(id, opts) {
|
||||||
if (!id.endsWith('.astro')) {
|
if (!id.endsWith('.astro')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -47,12 +62,20 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
|
||||||
internalURL: 'astro/internal',
|
internalURL: 'astro/internal',
|
||||||
preprocessStyle: async (value: string, attrs: Record<string, string>) => {
|
preprocessStyle: async (value: string, attrs: Record<string, string>) => {
|
||||||
if (!attrs || !attrs.lang) return null;
|
if (!attrs || !attrs.lang) return null;
|
||||||
const result = await transformWithVite(value, attrs, id, viteTransform);
|
const result = await transformWithVite({ value, attrs, id, transformHook: viteTransform, ssr: isSSR(opts) });
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// TODO: compiler supports `null`, but types don't yet
|
// TODO: compiler supports `null`, but types don't yet
|
||||||
return result as any;
|
return result as any;
|
||||||
}
|
}
|
||||||
return { code: result.code, map: result.map?.toString() };
|
let map: SourceMapInput | undefined;
|
||||||
|
if (result.map) {
|
||||||
|
if (typeof result.map === 'string') {
|
||||||
|
map = result.map;
|
||||||
|
} else if (result.map.mappings) {
|
||||||
|
map = result.map.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { code: result.code, map };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Compile `.ts` to `.js`
|
// Compile `.ts` to `.js`
|
||||||
|
@ -63,12 +86,14 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
|
||||||
map,
|
map,
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// if esbuild threw the error, find original code source to display
|
// if esbuild threw the error, find original code source to display (if it’s mapped)
|
||||||
if (err.errors && tsResult?.map) {
|
if (err.errors && tsResult?.map) {
|
||||||
const json = JSON.parse(tsResult.map);
|
const json = JSON.parse(tsResult.map);
|
||||||
const mappings = decode(json.mappings);
|
const mappings = decode(json.mappings);
|
||||||
const focusMapping = mappings[err.errors[0].location.line + 1];
|
const focusMapping = mappings[err.errors[0].location.line + 1];
|
||||||
err.sourceLoc = { file: id, line: (focusMapping[0][2] || 0) + 1, column: (focusMapping[0][3] || 0) + 1 };
|
if (Array.isArray(focusMapping) && focusMapping.length) {
|
||||||
|
err.sourceLoc = { file: id, line: (focusMapping[0][2] || 0) + 1, column: (focusMapping[0][3] || 0) + 1 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import type vite from '../core/vite';
|
import type vite from '../core/vite';
|
||||||
|
|
||||||
export type TransformHook = (code: string, id: string, ssr?: boolean) => Promise<vite.TransformResult>;
|
import { PREPROCESSOR_EXTENSIONS } from '../core/ssr/css.js';
|
||||||
|
|
||||||
// https://vitejs.dev/guide/features.html#css-pre-processors
|
export type TransformHook = (code: string, id: string, ssr?: boolean) => Promise<vite.TransformResult>;
|
||||||
const SUPPORTED_PREPROCESSORS = new Set(['scss', 'sass', 'styl', 'stylus', 'less']);
|
|
||||||
|
|
||||||
/** Load vite:css’ transform() hook */
|
/** Load vite:css’ transform() hook */
|
||||||
export function getViteTransform(viteConfig: vite.ResolvedConfig): TransformHook {
|
export function getViteTransform(viteConfig: vite.ResolvedConfig): TransformHook {
|
||||||
|
@ -13,10 +12,19 @@ export function getViteTransform(viteConfig: vite.ResolvedConfig): TransformHook
|
||||||
return viteCSSPlugin.transform.bind(null as any) as any;
|
return viteCSSPlugin.transform.bind(null as any) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Transform style using Vite hook */
|
interface TransformWithViteOptions {
|
||||||
export async function transformWithVite(value: string, attrs: Record<string, string>, id: string, transformHook: TransformHook): Promise<vite.TransformResult | null> {
|
value: string;
|
||||||
const lang = (attrs.lang || '').toLowerCase(); // don’t be case-sensitive
|
attrs: Record<string, string>;
|
||||||
if (!SUPPORTED_PREPROCESSORS.has(lang)) return null; // only preprocess the above
|
id: string;
|
||||||
const result = await transformHook(value, id.replace(/\.astro$/, `.${lang}`));
|
transformHook: TransformHook;
|
||||||
return result || null;
|
ssr?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform style using Vite hook */
|
||||||
|
export async function transformWithVite({ value, attrs, transformHook, id, ssr }: TransformWithViteOptions): Promise<vite.TransformResult | null> {
|
||||||
|
const lang = (`.${attrs.lang}` || '').toLowerCase(); // add leading "."; don’t be case-sensitive
|
||||||
|
if (!PREPROCESSOR_EXTENSIONS.has(lang)) {
|
||||||
|
return null; // only preprocess langs supported by Vite
|
||||||
|
}
|
||||||
|
return transformHook(value, id + `?astro&type=style&lang${lang}`, ssr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,25 +10,24 @@ before(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Styles SSR', () => {
|
describe('Styles SSR', () => {
|
||||||
// TODO: convert <style> to <link>
|
it('Has <link> tags', async () => {
|
||||||
// it('Has <link> tags', async () => {
|
const MUST_HAVE_LINK_TAGS = [
|
||||||
// const MUST_HAVE_LINK_TAGS = [
|
'/src/components/ReactCSS.css',
|
||||||
// '/src/components/ReactCSS.css',
|
'/src/components/ReactModules.module.css',
|
||||||
// '/src/components/ReactModules.module.css',
|
'/src/components/SvelteScoped.svelte',
|
||||||
// '/src/components/SvelteScoped.css',
|
'/src/components/VueCSS.vue',
|
||||||
// '/src/components/VueCSS.css',
|
'/src/components/VueModules.vue',
|
||||||
// '/src/components/VueModules.css',
|
'/src/components/VueScoped.vue',
|
||||||
// '/src/components/VueScoped.css',
|
];
|
||||||
// ];
|
|
||||||
|
|
||||||
// const html = await fixture.readFile('/index.html');
|
const html = await fixture.readFile('/index.html');
|
||||||
// const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
// for (const href of MUST_HAVE_LINK_TAGS) {
|
for (const href of [...$('link[rel="stylesheet"]')].map((el) => el.attribs.href)) {
|
||||||
// const el = $(`link[href="${href}"]`);
|
const hasTag = MUST_HAVE_LINK_TAGS.some((mustHaveHref) => href.includes(mustHaveHref));
|
||||||
// expect(el).to.have.lengthOf(1);
|
expect(hasTag).to.equal(true);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
it('Has correct CSS classes', async () => {
|
it('Has correct CSS classes', async () => {
|
||||||
const html = await fixture.readFile('/index.html');
|
const html = await fixture.readFile('/index.html');
|
||||||
|
@ -67,7 +66,7 @@ describe('Styles SSR', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('CSS Module support in .astro', async () => {
|
it('CSS scoped support in .astro', async () => {
|
||||||
const html = await fixture.readFile('/index.html');
|
const html = await fixture.readFile('/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ describe('Styles SSR', () => {
|
||||||
expect(wrapper).to.have.lengthOf(1);
|
expect(wrapper).to.have.lengthOf(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: fix compiler bug
|
// TODO: add dynamic class scoping in compiler
|
||||||
it.skip('Astro scoped styles', async () => {
|
it.skip('Astro scoped styles', async () => {
|
||||||
const html = await fixture.readFile('/index.html');
|
const html = await fixture.readFile('/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
@ -105,14 +104,14 @@ describe('Styles SSR', () => {
|
||||||
return match;
|
return match;
|
||||||
});
|
});
|
||||||
|
|
||||||
// test 1: Astro component missing scoped class
|
// test 1: Astro component has some scoped class
|
||||||
expect(scopedClass).to.equal(``);
|
expect(scopedClass).to.be.ok();
|
||||||
|
|
||||||
// test 2–3: children get scoped class
|
// test 2–3: children get scoped class
|
||||||
expect(el1.attr('class')).to.equal(`blue ${scopedClass}`);
|
expect(el1.attr('class')).to.equal(`blue ${scopedClass}`);
|
||||||
expect(el2.attr('class')).to.equal(`visible ${scopedClass}`);
|
expect(el2.attr('class')).to.equal(`visible ${scopedClass}`);
|
||||||
|
|
||||||
const { contents: css } = await fixture.fetch('/src/components/Astro.astro.css').then((res) => res.text());
|
const css = $('style').text();
|
||||||
|
|
||||||
// test 4: CSS generates as expected
|
// test 4: CSS generates as expected
|
||||||
expect(css.toString()).to.equal(`.blue.${scopedClass}{color:powderblue}.color\\:blue.${scopedClass}{color:powderblue}.visible.${scopedClass}{display:block}`);
|
expect(css.toString()).to.equal(`.blue.${scopedClass}{color:powderblue}.color\\:blue.${scopedClass}{color:powderblue}.visible.${scopedClass}{display:block}`);
|
||||||
|
@ -126,7 +125,7 @@ describe('Styles SSR', () => {
|
||||||
expect($('#no-scope').attr('class')).to.equal(undefined);
|
expect($('#no-scope').attr('class')).to.equal(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: fix compiler bug
|
// TODO: add behavior in compiler
|
||||||
it.skip('Astro scoped styles can be passed to child components', async () => {
|
it.skip('Astro scoped styles can be passed to child components', async () => {
|
||||||
const html = await fixture.readFile('/index.html');
|
const html = await fixture.readFile('/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<h1 id="svelte-scoped" class="svelte-title">Svelte Scoped CSS</h1>
|
<h1 id="svelte-scoped" class="svelte-title">Svelte Scoped CSS</h1>
|
||||||
|
|
||||||
<style>
|
<style lang="scss">
|
||||||
.svelte-title {
|
.svelte-title {
|
||||||
font-family: 'Comic Sans MS', sans-serif;
|
font-family: 'Comic Sans MS', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<style module>
|
<style module lang="scss">
|
||||||
|
$type: cursive;
|
||||||
.title {
|
.title {
|
||||||
font-family: cursive;
|
font-family: $type;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,7 @@ describe('Preact component', () => {
|
||||||
expect($('#class-component')).to.have.lengthOf(1);
|
expect($('#class-component')).to.have.lengthOf(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: fix compiler bug (not interpreting <ArrowFunction /> as a component)
|
it('Can load function component', async () => {
|
||||||
it.skip('Can load function component', async () => {
|
|
||||||
const html = await fixture.readFile('/fn/index.html');
|
const html = await fixture.readFile('/fn/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,8 @@ describe('React Components', () => {
|
||||||
// test 2: no reactroot
|
// test 2: no reactroot
|
||||||
expect($('#react-h2').attr('data-reactroot')).to.equal(undefined);
|
expect($('#react-h2').attr('data-reactroot')).to.equal(undefined);
|
||||||
|
|
||||||
// TODO: fix compiler bug with arrow components
|
|
||||||
// test 3: Can use function components
|
// test 3: Can use function components
|
||||||
// expect($('#arrow-fn-component')).to.have.lengthOf(1);
|
expect($('#arrow-fn-component')).to.have.lengthOf(1);
|
||||||
|
|
||||||
// test 4: Can use spread for components
|
// test 4: Can use spread for components
|
||||||
expect($('#component-spread-props')).to.have.lengthOf(1);
|
expect($('#component-spread-props')).to.have.lengthOf(1);
|
||||||
|
|
35
yarn.lock
35
yarn.lock
|
@ -106,10 +106,10 @@
|
||||||
"@algolia/logger-common" "4.10.5"
|
"@algolia/logger-common" "4.10.5"
|
||||||
"@algolia/requester-common" "4.10.5"
|
"@algolia/requester-common" "4.10.5"
|
||||||
|
|
||||||
"@astrojs/compiler@^0.2.16":
|
"@astrojs/compiler@^0.2.17":
|
||||||
version "0.2.16"
|
version "0.2.17"
|
||||||
resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.2.16.tgz#e2b560d699c586bb26e5332255050f8b97d1a19d"
|
resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.2.17.tgz#bb814a70af9c3694160d9fe8f7a0c96b4b194429"
|
||||||
integrity sha512-PVMIxBePkzxkg56g9WJXmKkeW0xCmAMOrmSpW0uySucWbdyAMc31sSZb9v6dhYt4lrFiV6CDOCCqcEmRc2wHoA==
|
integrity sha512-qWGC1UT0olUR/iXHOrD4tnwJSttiVDWfY4KDodhzTkctaXqpc69dBb3gpN8r1jiqZQCPT9Wqmb9GSKWqbUDn2A==
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript "^4.3.5"
|
typescript "^4.3.5"
|
||||||
|
|
||||||
|
@ -3876,14 +3876,14 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
||||||
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
||||||
|
|
||||||
domhandler@^4.0.0, domhandler@^4.2.0:
|
domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.2.2:
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
|
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
|
||||||
integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
|
integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
|
|
||||||
domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0:
|
domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0, domutils@^2.8.0:
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
||||||
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
||||||
|
@ -4001,6 +4001,11 @@ entities@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
||||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||||
|
|
||||||
|
entities@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
|
||||||
|
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
|
||||||
|
|
||||||
env-paths@^2.2.0:
|
env-paths@^2.2.0:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||||
|
@ -5592,6 +5597,16 @@ htmlparser2@^6.1.0:
|
||||||
domutils "^2.5.2"
|
domutils "^2.5.2"
|
||||||
entities "^2.0.0"
|
entities "^2.0.0"
|
||||||
|
|
||||||
|
htmlparser2@^7.1.2:
|
||||||
|
version "7.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.1.2.tgz#587923d38f03bc89e03076e00cba2c7473f37f7c"
|
||||||
|
integrity sha512-d6cqsbJba2nRdg8WW2okyD4ceonFHn9jLFxhwlNcLhQWcFPdxXeJulgOLjLKtAK9T6ahd+GQNZwG9fjmGW7lyg==
|
||||||
|
dependencies:
|
||||||
|
domelementtype "^2.0.1"
|
||||||
|
domhandler "^4.2.2"
|
||||||
|
domutils "^2.8.0"
|
||||||
|
entities "^3.0.1"
|
||||||
|
|
||||||
http-assert@^1.3.0:
|
http-assert@^1.3.0:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
|
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
|
||||||
|
@ -9539,10 +9554,10 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1,
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
sass@^1.43.2:
|
sass@^1.43.3:
|
||||||
version "1.43.2"
|
version "1.43.3"
|
||||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.2.tgz#c02501520c624ad6622529a8b3724eb08da82d65"
|
resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.3.tgz#aa16a69131b84f0cd23189a242571e8905f1ce43"
|
||||||
integrity sha512-DncYhjl3wBaPMMJR0kIUaH3sF536rVrOcqqVGmTZHQRRzj7LQlyGV7Mb8aCKFyILMr5VsPHwRYtyKpnKYlmQSQ==
|
integrity sha512-BJnLngqWpMeS65UvlYYEuCb3/fLxDxhHtOB/gWPxs6NKrslTxGt3ZxwIvOe/0Jm4tWwM/+tIpE3wj4dLEhPDeQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar ">=3.0.0 <4.0.0"
|
chokidar ">=3.0.0 <4.0.0"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue