Add .html
support (#3867)
* feat: add html package * feat: support assets in HTML * feat(html): upgrade html integration * feat(html): add `@astrojs/html` integration * feat(html): add html support to astro core * test(html): update html tests with package.json files * chore: add changeset * fix: remove import cycle * chore: fix types * refactor: remove @astrojs/html, add to core * chore: update types for `*.html` * fix: move *.html to astro/env Co-authored-by: Nate Moore <nate@astro.build>
This commit is contained in:
parent
8b468ccccc
commit
7250e4e86d
32 changed files with 534 additions and 12 deletions
5
.changeset/strong-stingrays-compete.md
Normal file
5
.changeset/strong-stingrays-compete.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add support for `.html` components and pages
|
5
packages/astro/env.d.ts
vendored
5
packages/astro/env.d.ts
vendored
|
@ -26,3 +26,8 @@ declare module '*.md' {
|
||||||
const load: MD['default'];
|
const load: MD['default'];
|
||||||
export default load;
|
export default load;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "*.html" {
|
||||||
|
const Component: { render(opts: { slots: Record<string, string> }): string };
|
||||||
|
export default Component;
|
||||||
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@
|
||||||
"prismjs": "^1.28.0",
|
"prismjs": "^1.28.0",
|
||||||
"prompts": "^2.4.2",
|
"prompts": "^2.4.2",
|
||||||
"recast": "^0.20.5",
|
"recast": "^0.20.5",
|
||||||
|
"rehype": "^12.0.1",
|
||||||
"resolve": "^1.22.0",
|
"resolve": "^1.22.0",
|
||||||
"rollup": "^2.75.6",
|
"rollup": "^2.75.6",
|
||||||
"semver": "^7.3.7",
|
"semver": "^7.3.7",
|
||||||
|
@ -136,6 +137,8 @@
|
||||||
"strip-ansi": "^7.0.1",
|
"strip-ansi": "^7.0.1",
|
||||||
"supports-esm": "^1.0.0",
|
"supports-esm": "^1.0.0",
|
||||||
"tsconfig-resolver": "^3.0.1",
|
"tsconfig-resolver": "^3.0.1",
|
||||||
|
"unist-util-visit": "^4.1.0",
|
||||||
|
"vfile": "^5.3.2",
|
||||||
"vite": "3.0.2",
|
"vite": "3.0.2",
|
||||||
"yargs-parser": "^21.0.1",
|
"yargs-parser": "^21.0.1",
|
||||||
"zod": "^3.17.3"
|
"zod": "^3.17.3"
|
||||||
|
|
|
@ -325,7 +325,7 @@ export async function validateConfig(
|
||||||
const result = {
|
const result = {
|
||||||
...(await AstroConfigRelativeSchema.parseAsync(userConfig)),
|
...(await AstroConfigRelativeSchema.parseAsync(userConfig)),
|
||||||
_ctx: {
|
_ctx: {
|
||||||
pageExtensions: ['.astro', '.md'],
|
pageExtensions: ['.astro', '.md', '.html'],
|
||||||
scripts: [],
|
scripts: [],
|
||||||
renderers: [],
|
renderers: [],
|
||||||
injectedRoutes: [],
|
injectedRoutes: [],
|
||||||
|
|
|
@ -11,6 +11,7 @@ import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
|
||||||
import envVitePlugin from '../vite-plugin-env/index.js';
|
import envVitePlugin from '../vite-plugin-env/index.js';
|
||||||
import astroIntegrationsContainerPlugin from '../vite-plugin-integrations-container/index.js';
|
import astroIntegrationsContainerPlugin from '../vite-plugin-integrations-container/index.js';
|
||||||
import jsxVitePlugin from '../vite-plugin-jsx/index.js';
|
import jsxVitePlugin from '../vite-plugin-jsx/index.js';
|
||||||
|
import htmlVitePlugin from '../vite-plugin-html/index.js';
|
||||||
import markdownVitePlugin from '../vite-plugin-markdown/index.js';
|
import markdownVitePlugin from '../vite-plugin-markdown/index.js';
|
||||||
import astroScriptsPlugin from '../vite-plugin-scripts/index.js';
|
import astroScriptsPlugin from '../vite-plugin-scripts/index.js';
|
||||||
import { createCustomViteLogger } from './errors.js';
|
import { createCustomViteLogger } from './errors.js';
|
||||||
|
@ -73,6 +74,7 @@ export async function createVite(
|
||||||
mode === 'dev' && astroViteServerPlugin({ config: astroConfig, logging }),
|
mode === 'dev' && astroViteServerPlugin({ config: astroConfig, logging }),
|
||||||
envVitePlugin({ config: astroConfig }),
|
envVitePlugin({ config: astroConfig }),
|
||||||
markdownVitePlugin({ config: astroConfig }),
|
markdownVitePlugin({ config: astroConfig }),
|
||||||
|
htmlVitePlugin(),
|
||||||
jsxVitePlugin({ config: astroConfig, logging }),
|
jsxVitePlugin({ config: astroConfig, logging }),
|
||||||
astroPostprocessVitePlugin({ config: astroConfig }),
|
astroPostprocessVitePlugin({ config: astroConfig }),
|
||||||
astroIntegrationsContainerPlugin({ config: astroConfig }),
|
astroIntegrationsContainerPlugin({ config: astroConfig }),
|
||||||
|
|
|
@ -45,14 +45,7 @@ export default async function dev(config: AstroConfig, options: DevOptions): Pro
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
server: { host },
|
server: { host },
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: [
|
include: rendererClientEntries,
|
||||||
'astro/client/idle.js',
|
|
||||||
'astro/client/load.js',
|
|
||||||
'astro/client/visible.js',
|
|
||||||
'astro/client/media.js',
|
|
||||||
'astro/client/only.js',
|
|
||||||
...rendererClientEntries,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ astroConfig: config, logging: options.logging, mode: 'dev' }
|
{ astroConfig: config, logging: options.logging, mode: 'dev' }
|
||||||
|
|
|
@ -58,7 +58,8 @@ export function fixViteErrorMessage(_err: unknown, server: ViteDevServer, filePa
|
||||||
const content = fs.readFileSync(fileURLToPath(filePath)).toString();
|
const content = fs.readFileSync(fileURLToPath(filePath)).toString();
|
||||||
const lns = content.split('\n');
|
const lns = content.split('\n');
|
||||||
const line = lns.findIndex((ln) => ln.includes(importName));
|
const line = lns.findIndex((ln) => ln.includes(importName));
|
||||||
const column = lns[line].indexOf(importName);
|
if (line == -1) return err;
|
||||||
|
const column = lns[line]?.indexOf(importName);
|
||||||
if (!(err as any).id) {
|
if (!(err as any).id) {
|
||||||
(err as any).id = `${fileURLToPath(filePath)}:${line + 1}:${column + 1}`;
|
(err as any).id = `${fileURLToPath(filePath)}:${line + 1}:${column + 1}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,6 +210,21 @@ export async function renderComponent(
|
||||||
return markHTMLString(children);
|
return markHTMLString(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Component && typeof Component === 'object' && (Component as any)['astro:html']) {
|
||||||
|
const children: Record<string, string> = {};
|
||||||
|
if (slots) {
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(slots).map(([key, value]) =>
|
||||||
|
renderSlot(result, value as string).then((output) => {
|
||||||
|
children[key] = output;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const html = (Component as any).render({ slots: children });
|
||||||
|
return markHTMLString(html);
|
||||||
|
}
|
||||||
|
|
||||||
if (Component && (Component as any).isAstroComponentFactory) {
|
if (Component && (Component as any).isAstroComponentFactory) {
|
||||||
async function* renderAstroComponentInline(): AsyncGenerator<string, void, undefined> {
|
async function* renderAstroComponentInline(): AsyncGenerator<string, void, undefined> {
|
||||||
let iterable = await renderToIterable(result, Component as any, _props, slots);
|
let iterable = await renderToIterable(result, Component as any, _props, slots);
|
||||||
|
@ -265,6 +280,7 @@ Did you mean to add ${formatList(probableRendererNames.map((r) => '`' + r + '`')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the renderers `check` hook to see if any claim this component.
|
// Call the renderers `check` hook to see if any claim this component.
|
||||||
let renderer: SSRLoadedRenderer | undefined;
|
let renderer: SSRLoadedRenderer | undefined;
|
||||||
if (metadata.hydrate !== 'only') {
|
if (metadata.hydrate !== 'only') {
|
||||||
|
|
14
packages/astro/src/vite-plugin-html/index.ts
Normal file
14
packages/astro/src/vite-plugin-html/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { transform } from './transform/index.js';
|
||||||
|
|
||||||
|
export default function html() {
|
||||||
|
return {
|
||||||
|
name: 'astro:html',
|
||||||
|
options(options: any) {
|
||||||
|
options.plugins = options.plugins?.filter((p: any) => p.name !== 'vite:build-html');
|
||||||
|
},
|
||||||
|
async transform(source: string, id: string) {
|
||||||
|
if (!id.endsWith('.html')) return;
|
||||||
|
return await transform(source, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
packages/astro/src/vite-plugin-html/transform/escape.ts
Normal file
27
packages/astro/src/vite-plugin-html/transform/escape.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import type { Plugin } from 'unified';
|
||||||
|
import type { Root, RootContent } from 'hast';
|
||||||
|
import type MagicString from 'magic-string';
|
||||||
|
import { visit } from 'unist-util-visit';
|
||||||
|
|
||||||
|
import { replaceAttribute, needsEscape, escape } from './utils.js';
|
||||||
|
|
||||||
|
const rehypeEscape: Plugin<[{ s: MagicString }], Root> = ({ s }) => {
|
||||||
|
return (tree, file) => {
|
||||||
|
visit(tree, (node: Root | RootContent, index, parent) => {
|
||||||
|
if (node.type === 'text' || node.type === 'comment') {
|
||||||
|
if (needsEscape(node.value)) {
|
||||||
|
s.overwrite(node.position!.start.offset!, node.position!.end.offset!, escape(node.value));
|
||||||
|
}
|
||||||
|
} else if (node.type === 'element') {
|
||||||
|
for (const [key, value] of Object.entries(node.properties ?? {})) {
|
||||||
|
const newKey = needsEscape(key) ? escape(key) : key;
|
||||||
|
const newValue = needsEscape(value) ? escape(value) : value;
|
||||||
|
if (newKey === key && newValue === value) continue;
|
||||||
|
replaceAttribute(s, node, key, (value === '') ? newKey : `${newKey}="${newValue}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default rehypeEscape;
|
32
packages/astro/src/vite-plugin-html/transform/index.ts
Normal file
32
packages/astro/src/vite-plugin-html/transform/index.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import MagicString from 'magic-string';
|
||||||
|
import { rehype } from 'rehype';
|
||||||
|
import { VFile } from 'vfile';
|
||||||
|
import escape from './escape.js';
|
||||||
|
import slots, { SLOT_PREFIX } from './slots.js';
|
||||||
|
|
||||||
|
export async function transform(code: string, id: string) {
|
||||||
|
const s = new MagicString(code, { filename: id });
|
||||||
|
const imports = new Map();
|
||||||
|
const parser = rehype()
|
||||||
|
.data('settings', { fragment: true })
|
||||||
|
.use(escape, { s })
|
||||||
|
.use(slots, { s });
|
||||||
|
|
||||||
|
const vfile = new VFile({ value: code, path: id })
|
||||||
|
await parser.process(vfile)
|
||||||
|
s.prepend(`export default {\n\t"astro:html": true,\n\trender({ slots: ${SLOT_PREFIX} }) {\n\t\treturn \``);
|
||||||
|
s.append('`\n\t}\n}');
|
||||||
|
|
||||||
|
if (imports.size > 0) {
|
||||||
|
let importText = ''
|
||||||
|
for (const [path, importName] of imports.entries()) {
|
||||||
|
importText += `import ${importName} from "${path}";\n`
|
||||||
|
}
|
||||||
|
s.prepend(importText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: s.toString(),
|
||||||
|
map: s.generateMap()
|
||||||
|
}
|
||||||
|
}
|
27
packages/astro/src/vite-plugin-html/transform/slots.ts
Normal file
27
packages/astro/src/vite-plugin-html/transform/slots.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import type { Plugin } from 'unified';
|
||||||
|
import type { Root, RootContent } from 'hast';
|
||||||
|
|
||||||
|
import { visit } from 'unist-util-visit';
|
||||||
|
import MagicString from 'magic-string';
|
||||||
|
import { escape } from './utils.js';
|
||||||
|
|
||||||
|
const rehypeSlots: Plugin<[{ s: MagicString }], Root> = ({ s }) => {
|
||||||
|
return (tree, file) => {
|
||||||
|
visit(tree, (node: Root | RootContent, index, parent) => {
|
||||||
|
if (node.type === 'element' && node.tagName === 'slot') {
|
||||||
|
if (typeof node.properties?.['is:inline'] !== 'undefined') return;
|
||||||
|
const name = node.properties?.['name'] ?? 'default';
|
||||||
|
const start = node.position?.start.offset ?? 0;
|
||||||
|
const end = node.position?.end.offset ?? 0;
|
||||||
|
const first = node.children.at(0) ?? node;
|
||||||
|
const last = node.children.at(-1) ?? node;
|
||||||
|
const text = file.value.slice(first.position?.start.offset ?? 0, last.position?.end.offset ?? 0).toString();
|
||||||
|
s.overwrite(start, end, `\${${SLOT_PREFIX}["${name}"] ?? \`${escape(text).trim()}\`}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default rehypeSlots;
|
||||||
|
|
||||||
|
export const SLOT_PREFIX = `___SLOTS___`;
|
27
packages/astro/src/vite-plugin-html/transform/utils.ts
Normal file
27
packages/astro/src/vite-plugin-html/transform/utils.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import type { Element } from 'hast';
|
||||||
|
import MagicString from 'magic-string';
|
||||||
|
|
||||||
|
const splitAttrsTokenizer = /([\$\{\}\@a-z0-9_\:\-]*)\s*?=\s*?(['"]?)(.*?)\2\s+/gim;
|
||||||
|
|
||||||
|
export function replaceAttribute(s: MagicString, node: Element, key: string, newValue: string) {
|
||||||
|
splitAttrsTokenizer.lastIndex = 0;
|
||||||
|
const text = s.original.slice(node.position?.start.offset ?? 0, node.position?.end.offset ?? 0).toString();
|
||||||
|
const offset = text.indexOf(key);
|
||||||
|
if (offset === -1) return;
|
||||||
|
const start = node.position!.start.offset! + offset;
|
||||||
|
const tokens = text.slice(offset).split(splitAttrsTokenizer);
|
||||||
|
const token = tokens[0].replace(/([^>])(\>[\s\S]*$)/gmi, '$1');
|
||||||
|
if (token.trim() === key) {
|
||||||
|
const end = start + key.length;
|
||||||
|
s.overwrite(start, end, newValue)
|
||||||
|
} else {
|
||||||
|
const end = start + `${key}=${tokens[2]}${tokens[3]}${tokens[2]}`.length;
|
||||||
|
s.overwrite(start, end, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function needsEscape(value: any): value is string {
|
||||||
|
return typeof value === 'string' && (value.includes('`') || value.includes('${'));
|
||||||
|
}
|
||||||
|
export function escape(value: string) {
|
||||||
|
return value.replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
||||||
|
}
|
8
packages/astro/test/fixtures/html-component/package.json
vendored
Normal file
8
packages/astro/test/fixtures/html-component/package.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "@test/html-component",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
3
packages/astro/test/fixtures/html-component/src/components/Test.html
vendored
Normal file
3
packages/astro/test/fixtures/html-component/src/components/Test.html
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<h1>Hello component!</h1>
|
||||||
|
|
||||||
|
<div id="foo">bar</div>
|
5
packages/astro/test/fixtures/html-component/src/pages/index.astro
vendored
Normal file
5
packages/astro/test/fixtures/html-component/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
import Test from '../components/Test.html';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Test />
|
8
packages/astro/test/fixtures/html-escape/package.json
vendored
Normal file
8
packages/astro/test/fixtures/html-escape/package.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "@test/html-escape",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
4
packages/astro/test/fixtures/html-escape/src/components/Test.html
vendored
Normal file
4
packages/astro/test/fixtures/html-escape/src/components/Test.html
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div>${foo}</div>
|
||||||
|
<span ${attr}></span>
|
||||||
|
<custom-element x-data="`${test}`"></custom-element>
|
||||||
|
<script>console.log(`hello ${"world"}!`)</script>
|
5
packages/astro/test/fixtures/html-escape/src/pages/index.astro
vendored
Normal file
5
packages/astro/test/fixtures/html-escape/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
import Test from '../components/Test.html';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Test />
|
8
packages/astro/test/fixtures/html-page/package.json
vendored
Normal file
8
packages/astro/test/fixtures/html-page/package.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "@test/html-page",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
1
packages/astro/test/fixtures/html-page/src/pages/index.html
vendored
Normal file
1
packages/astro/test/fixtures/html-page/src/pages/index.html
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<h1>Hello page!</h1>
|
8
packages/astro/test/fixtures/html-slots/package.json
vendored
Normal file
8
packages/astro/test/fixtures/html-slots/package.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "@test/html-slots",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
1
packages/astro/test/fixtures/html-slots/src/components/Default.html
vendored
Normal file
1
packages/astro/test/fixtures/html-slots/src/components/Default.html
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<div id="default"><slot></slot></div>
|
1
packages/astro/test/fixtures/html-slots/src/components/Inline.html
vendored
Normal file
1
packages/astro/test/fixtures/html-slots/src/components/Inline.html
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<div id="inline"><slot is:inline></slot></div>
|
3
packages/astro/test/fixtures/html-slots/src/components/Named.html
vendored
Normal file
3
packages/astro/test/fixtures/html-slots/src/components/Named.html
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<div id="a"><slot name="a"></slot></div>
|
||||||
|
<div id="b"><slot name="b"></slot></div>
|
||||||
|
<div id="c"><slot name="c"></slot></div>
|
13
packages/astro/test/fixtures/html-slots/src/pages/index.astro
vendored
Normal file
13
packages/astro/test/fixtures/html-slots/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
import Default from '../components/Default.html';
|
||||||
|
import Named from '../components/Named.html';
|
||||||
|
import Inline from '../components/Inline.html';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Default>Default</Default>
|
||||||
|
<Named>
|
||||||
|
<span slot="a">A</span>
|
||||||
|
<span slot="b">B</span>
|
||||||
|
<span slot="c">C</span>
|
||||||
|
</Named>
|
||||||
|
<Inline></Inline>
|
57
packages/astro/test/html-component.test.js
Normal file
57
packages/astro/test/html-component.test.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as cheerio from 'cheerio';
|
||||||
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
describe('HTML Component', () => {
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/html-component/',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('build', () => {
|
||||||
|
before(async () => {
|
||||||
|
await fixture.build();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const html = await fixture.readFile('/index.html');
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
const h1 = $('h1');
|
||||||
|
const foo = $('#foo');
|
||||||
|
|
||||||
|
expect(h1.text()).to.equal('Hello component!');
|
||||||
|
expect(foo.text()).to.equal('bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dev', () => {
|
||||||
|
let devServer;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
devServer = await fixture.startDevServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await devServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const res = await fixture.fetch('/');
|
||||||
|
|
||||||
|
expect(res.status).to.equal(200);
|
||||||
|
|
||||||
|
const html = await res.text();
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
const h1 = $('h1');
|
||||||
|
const foo = $('#foo');
|
||||||
|
|
||||||
|
expect(h1.text()).to.equal('Hello component!');
|
||||||
|
expect(foo.text()).to.equal('bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
69
packages/astro/test/html-escape.test.js
Normal file
69
packages/astro/test/html-escape.test.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as cheerio from 'cheerio';
|
||||||
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
describe('HTML Escape', () => {
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/html-escape/',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('build', () => {
|
||||||
|
before(async () => {
|
||||||
|
await fixture.build();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const html = await fixture.readFile('/index.html');
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
const div = $('div');
|
||||||
|
expect(div.text()).to.equal('${foo}');
|
||||||
|
|
||||||
|
const span = $('span');
|
||||||
|
expect(span.attr('${attr}')).to.equal("");
|
||||||
|
|
||||||
|
const ce = $('custom-element');
|
||||||
|
expect(ce.attr('x-data')).to.equal("`${test}`");
|
||||||
|
|
||||||
|
const script = $('script');
|
||||||
|
expect(script.text()).to.equal('console.log(`hello ${"world"}!`)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dev', () => {
|
||||||
|
let devServer;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
devServer = await fixture.startDevServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await devServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const res = await fixture.fetch('/');
|
||||||
|
|
||||||
|
expect(res.status).to.equal(200);
|
||||||
|
|
||||||
|
const html = await res.text();
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
const div = $('div');
|
||||||
|
expect(div.text()).to.equal('${foo}');
|
||||||
|
|
||||||
|
const span = $('span');
|
||||||
|
expect(span.attr('${attr}')).to.equal("");
|
||||||
|
|
||||||
|
const ce = $('custom-element');
|
||||||
|
expect(ce.attr('x-data')).to.equal("`${test}`");
|
||||||
|
|
||||||
|
const script = $('script');
|
||||||
|
expect(script.text()).to.equal('console.log(`hello ${"world"}!`)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
53
packages/astro/test/html-page.test.js
Normal file
53
packages/astro/test/html-page.test.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as cheerio from 'cheerio';
|
||||||
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
describe('HTML Page', () => {
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/html-page/',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('build', () => {
|
||||||
|
before(async () => {
|
||||||
|
await fixture.build();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const html = await fixture.readFile('/index.html');
|
||||||
|
const $ = cheerio.load(html)
|
||||||
|
|
||||||
|
const h1 = $('h1');
|
||||||
|
|
||||||
|
expect(h1.text()).to.equal('Hello page!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dev', () => {
|
||||||
|
let devServer;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
devServer = await fixture.startDevServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await devServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const res = await fixture.fetch('/');
|
||||||
|
|
||||||
|
expect(res.status).to.equal(200);
|
||||||
|
|
||||||
|
const html = await res.text();
|
||||||
|
const $ = cheerio.load(html)
|
||||||
|
|
||||||
|
const h1 = $('h1');
|
||||||
|
|
||||||
|
expect(h1.text()).to.equal('Hello page!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
75
packages/astro/test/html-slots.test.js
Normal file
75
packages/astro/test/html-slots.test.js
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as cheerio from 'cheerio';
|
||||||
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
describe('HTML Slots', () => {
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/html-slots/',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('build', () => {
|
||||||
|
before(async () => {
|
||||||
|
await fixture.build();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const html = await fixture.readFile('/index.html');
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
const slotDefault = $('#default');
|
||||||
|
expect(slotDefault.text()).to.equal('Default');
|
||||||
|
|
||||||
|
const a = $('#a');
|
||||||
|
expect(a.text().trim()).to.equal('A');
|
||||||
|
|
||||||
|
const b = $('#b');
|
||||||
|
expect(b.text().trim()).to.equal('B');
|
||||||
|
|
||||||
|
const c = $('#c');
|
||||||
|
expect(c.text().trim()).to.equal('C');
|
||||||
|
|
||||||
|
const inline = $('#inline');
|
||||||
|
expect(inline.html()).to.equal('<slot is:inline=""></slot>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dev', () => {
|
||||||
|
let devServer;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
devServer = await fixture.startDevServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await devServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
const res = await fixture.fetch('/');
|
||||||
|
|
||||||
|
expect(res.status).to.equal(200);
|
||||||
|
|
||||||
|
const html = await res.text();
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
const slotDefault = $('#default');
|
||||||
|
expect(slotDefault.text()).to.equal('Default');
|
||||||
|
|
||||||
|
const a = $('#a');
|
||||||
|
expect(a.text().trim()).to.equal('A');
|
||||||
|
|
||||||
|
const b = $('#b');
|
||||||
|
expect(b.text().trim()).to.equal('B');
|
||||||
|
|
||||||
|
const c = $('#c');
|
||||||
|
expect(c.text().trim()).to.equal('C');
|
||||||
|
|
||||||
|
const inline = $('#inline');
|
||||||
|
expect(inline.html()).to.equal('<slot is:inline=""></slot>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -506,6 +506,7 @@ importers:
|
||||||
prismjs: ^1.28.0
|
prismjs: ^1.28.0
|
||||||
prompts: ^2.4.2
|
prompts: ^2.4.2
|
||||||
recast: ^0.20.5
|
recast: ^0.20.5
|
||||||
|
rehype: ^12.0.1
|
||||||
resolve: ^1.22.0
|
resolve: ^1.22.0
|
||||||
rollup: ^2.75.6
|
rollup: ^2.75.6
|
||||||
sass: ^1.52.2
|
sass: ^1.52.2
|
||||||
|
@ -519,6 +520,8 @@ importers:
|
||||||
strip-ansi: ^7.0.1
|
strip-ansi: ^7.0.1
|
||||||
supports-esm: ^1.0.0
|
supports-esm: ^1.0.0
|
||||||
tsconfig-resolver: ^3.0.1
|
tsconfig-resolver: ^3.0.1
|
||||||
|
unist-util-visit: ^4.1.0
|
||||||
|
vfile: ^5.3.2
|
||||||
vite: 3.0.2
|
vite: 3.0.2
|
||||||
yargs-parser: ^21.0.1
|
yargs-parser: ^21.0.1
|
||||||
zod: ^3.17.3
|
zod: ^3.17.3
|
||||||
|
@ -566,6 +569,7 @@ importers:
|
||||||
prismjs: 1.28.0
|
prismjs: 1.28.0
|
||||||
prompts: 2.4.2
|
prompts: 2.4.2
|
||||||
recast: 0.20.5
|
recast: 0.20.5
|
||||||
|
rehype: 12.0.1
|
||||||
resolve: 1.22.1
|
resolve: 1.22.1
|
||||||
rollup: 2.76.0
|
rollup: 2.76.0
|
||||||
semver: 7.3.7
|
semver: 7.3.7
|
||||||
|
@ -577,6 +581,8 @@ importers:
|
||||||
strip-ansi: 7.0.1
|
strip-ansi: 7.0.1
|
||||||
supports-esm: 1.0.0
|
supports-esm: 1.0.0
|
||||||
tsconfig-resolver: 3.0.1
|
tsconfig-resolver: 3.0.1
|
||||||
|
unist-util-visit: 4.1.0
|
||||||
|
vfile: 5.3.4
|
||||||
vite: 3.0.2_sass@1.53.0
|
vite: 3.0.2_sass@1.53.0
|
||||||
yargs-parser: 21.0.1
|
yargs-parser: 21.0.1
|
||||||
zod: 3.17.3
|
zod: 3.17.3
|
||||||
|
@ -1526,6 +1532,30 @@ importers:
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/html-component:
|
||||||
|
specifiers:
|
||||||
|
astro: workspace:*
|
||||||
|
dependencies:
|
||||||
|
astro: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/html-escape:
|
||||||
|
specifiers:
|
||||||
|
astro: workspace:*
|
||||||
|
dependencies:
|
||||||
|
astro: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/html-page:
|
||||||
|
specifiers:
|
||||||
|
astro: workspace:*
|
||||||
|
dependencies:
|
||||||
|
astro: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/html-slots:
|
||||||
|
specifiers:
|
||||||
|
astro: workspace:*
|
||||||
|
dependencies:
|
||||||
|
astro: link:../../..
|
||||||
|
|
||||||
packages/astro/test/fixtures/import-ts-with-js:
|
packages/astro/test/fixtures/import-ts-with-js:
|
||||||
specifiers:
|
specifiers:
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
|
@ -9547,7 +9577,7 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/concat-map/0.0.1:
|
/concat-map/0.0.1:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
|
||||||
|
|
||||||
/concurrently/7.2.2:
|
/concurrently/7.2.2:
|
||||||
resolution: {integrity: sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw==}
|
resolution: {integrity: sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw==}
|
||||||
|
@ -14217,6 +14247,15 @@ packages:
|
||||||
unist-util-visit: 4.1.0
|
unist-util-visit: 4.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/rehype-parse/8.0.4:
|
||||||
|
resolution: {integrity: sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 2.3.4
|
||||||
|
hast-util-from-parse5: 7.1.0
|
||||||
|
parse5: 6.0.1
|
||||||
|
unified: 10.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/rehype-raw/6.1.1:
|
/rehype-raw/6.1.1:
|
||||||
resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==}
|
resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -14251,6 +14290,15 @@ packages:
|
||||||
'@jsdevtools/rehype-toc': 3.0.2
|
'@jsdevtools/rehype-toc': 3.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/rehype/12.0.1:
|
||||||
|
resolution: {integrity: sha512-ey6kAqwLM3X6QnMDILJthGvG1m1ULROS9NT4uG9IDCuv08SFyLlreSuvOa//DgEvbXx62DS6elGVqusWhRUbgw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 2.3.4
|
||||||
|
rehype-parse: 8.0.4
|
||||||
|
rehype-stringify: 9.0.3
|
||||||
|
unified: 10.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/remark-code-titles/0.1.2:
|
/remark-code-titles/0.1.2:
|
||||||
resolution: {integrity: sha512-KsHQbaI4FX8Ozxqk7YErxwmBiveUqloKuVqyPG2YPLHojpgomodWgRfG4B+bOtmn/5bfJ8khw4rR0lvgVFl2Uw==}
|
resolution: {integrity: sha512-KsHQbaI4FX8Ozxqk7YErxwmBiveUqloKuVqyPG2YPLHojpgomodWgRfG4B+bOtmn/5bfJ8khw4rR0lvgVFl2Uw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in a new issue