summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Nate Moore <natemoo-re@users.noreply.github.com> 2022-07-22 10:32:36 -0500
committerGravatar GitHub <noreply@github.com> 2022-07-22 10:32:36 -0500
commit7250e4e86da41e7c662afa4b67f7cefc7da15e69 (patch)
tree601206ba1e6604a2a3cf0cb7111738c85fbcc356
parent8b468ccccc0e47c3153175a3a9f3eadb191a8f0c (diff)
downloadastro-7250e4e86da41e7c662afa4b67f7cefc7da15e69.tar.gz
astro-7250e4e86da41e7c662afa4b67f7cefc7da15e69.tar.zst
astro-7250e4e86da41e7c662afa4b67f7cefc7da15e69.zip
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>
-rw-r--r--.changeset/strong-stingrays-compete.md5
-rw-r--r--packages/astro/env.d.ts5
-rw-r--r--packages/astro/package.json3
-rw-r--r--packages/astro/src/core/config.ts2
-rw-r--r--packages/astro/src/core/create-vite.ts2
-rw-r--r--packages/astro/src/core/dev/index.ts9
-rw-r--r--packages/astro/src/core/errors.ts3
-rw-r--r--packages/astro/src/runtime/server/index.ts16
-rw-r--r--packages/astro/src/vite-plugin-html/index.ts14
-rw-r--r--packages/astro/src/vite-plugin-html/transform/escape.ts27
-rw-r--r--packages/astro/src/vite-plugin-html/transform/index.ts32
-rw-r--r--packages/astro/src/vite-plugin-html/transform/slots.ts27
-rw-r--r--packages/astro/src/vite-plugin-html/transform/utils.ts27
-rw-r--r--packages/astro/test/fixtures/html-component/package.json8
-rw-r--r--packages/astro/test/fixtures/html-component/src/components/Test.html3
-rw-r--r--packages/astro/test/fixtures/html-component/src/pages/index.astro5
-rw-r--r--packages/astro/test/fixtures/html-escape/package.json8
-rw-r--r--packages/astro/test/fixtures/html-escape/src/components/Test.html4
-rw-r--r--packages/astro/test/fixtures/html-escape/src/pages/index.astro5
-rw-r--r--packages/astro/test/fixtures/html-page/package.json8
-rw-r--r--packages/astro/test/fixtures/html-page/src/pages/index.html1
-rw-r--r--packages/astro/test/fixtures/html-slots/package.json8
-rw-r--r--packages/astro/test/fixtures/html-slots/src/components/Default.html1
-rw-r--r--packages/astro/test/fixtures/html-slots/src/components/Inline.html1
-rw-r--r--packages/astro/test/fixtures/html-slots/src/components/Named.html3
-rw-r--r--packages/astro/test/fixtures/html-slots/src/pages/index.astro13
-rw-r--r--packages/astro/test/html-component.test.js57
-rw-r--r--packages/astro/test/html-escape.test.js69
-rw-r--r--packages/astro/test/html-page.test.js53
-rw-r--r--packages/astro/test/html-slots.test.js75
-rw-r--r--packages/webapi/mod.d.ts2
-rw-r--r--pnpm-lock.yaml50
32 files changed, 534 insertions, 12 deletions
diff --git a/.changeset/strong-stingrays-compete.md b/.changeset/strong-stingrays-compete.md
new file mode 100644
index 000000000..f01d4cc8c
--- /dev/null
+++ b/.changeset/strong-stingrays-compete.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Add support for `.html` components and pages
diff --git a/packages/astro/env.d.ts b/packages/astro/env.d.ts
index aa649c896..619a4422b 100644
--- a/packages/astro/env.d.ts
+++ b/packages/astro/env.d.ts
@@ -26,3 +26,8 @@ declare module '*.md' {
const load: MD['default'];
export default load;
}
+
+declare module "*.html" {
+ const Component: { render(opts: { slots: Record<string, string> }): string };
+ export default Component;
+}
diff --git a/packages/astro/package.json b/packages/astro/package.json
index 8d5a27e49..3e1e13fec 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -125,6 +125,7 @@
"prismjs": "^1.28.0",
"prompts": "^2.4.2",
"recast": "^0.20.5",
+ "rehype": "^12.0.1",
"resolve": "^1.22.0",
"rollup": "^2.75.6",
"semver": "^7.3.7",
@@ -136,6 +137,8 @@
"strip-ansi": "^7.0.1",
"supports-esm": "^1.0.0",
"tsconfig-resolver": "^3.0.1",
+ "unist-util-visit": "^4.1.0",
+ "vfile": "^5.3.2",
"vite": "3.0.2",
"yargs-parser": "^21.0.1",
"zod": "^3.17.3"
diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts
index 75fabdc89..bbfad0439 100644
--- a/packages/astro/src/core/config.ts
+++ b/packages/astro/src/core/config.ts
@@ -325,7 +325,7 @@ export async function validateConfig(
const result = {
...(await AstroConfigRelativeSchema.parseAsync(userConfig)),
_ctx: {
- pageExtensions: ['.astro', '.md'],
+ pageExtensions: ['.astro', '.md', '.html'],
scripts: [],
renderers: [],
injectedRoutes: [],
diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts
index c853b29c7..72e357470 100644
--- a/packages/astro/src/core/create-vite.ts
+++ b/packages/astro/src/core/create-vite.ts
@@ -11,6 +11,7 @@ import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
import envVitePlugin from '../vite-plugin-env/index.js';
import astroIntegrationsContainerPlugin from '../vite-plugin-integrations-container/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 astroScriptsPlugin from '../vite-plugin-scripts/index.js';
import { createCustomViteLogger } from './errors.js';
@@ -73,6 +74,7 @@ export async function createVite(
mode === 'dev' && astroViteServerPlugin({ config: astroConfig, logging }),
envVitePlugin({ config: astroConfig }),
markdownVitePlugin({ config: astroConfig }),
+ htmlVitePlugin(),
jsxVitePlugin({ config: astroConfig, logging }),
astroPostprocessVitePlugin({ config: astroConfig }),
astroIntegrationsContainerPlugin({ config: astroConfig }),
diff --git a/packages/astro/src/core/dev/index.ts b/packages/astro/src/core/dev/index.ts
index df26858e5..eff09c5a4 100644
--- a/packages/astro/src/core/dev/index.ts
+++ b/packages/astro/src/core/dev/index.ts
@@ -45,14 +45,7 @@ export default async function dev(config: AstroConfig, options: DevOptions): Pro
mode: 'development',
server: { host },
optimizeDeps: {
- include: [
- 'astro/client/idle.js',
- 'astro/client/load.js',
- 'astro/client/visible.js',
- 'astro/client/media.js',
- 'astro/client/only.js',
- ...rendererClientEntries,
- ],
+ include: rendererClientEntries,
},
},
{ astroConfig: config, logging: options.logging, mode: 'dev' }
diff --git a/packages/astro/src/core/errors.ts b/packages/astro/src/core/errors.ts
index 49bf4b6f2..13eec5cb8 100644
--- a/packages/astro/src/core/errors.ts
+++ b/packages/astro/src/core/errors.ts
@@ -58,7 +58,8 @@ export function fixViteErrorMessage(_err: unknown, server: ViteDevServer, filePa
const content = fs.readFileSync(fileURLToPath(filePath)).toString();
const lns = content.split('\n');
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) {
(err as any).id = `${fileURLToPath(filePath)}:${line + 1}:${column + 1}`;
}
diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts
index dc48c1ecb..9dbd4af41 100644
--- a/packages/astro/src/runtime/server/index.ts
+++ b/packages/astro/src/runtime/server/index.ts
@@ -210,6 +210,21 @@ export async function renderComponent(
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) {
async function* renderAstroComponentInline(): AsyncGenerator<string, void, undefined> {
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.
let renderer: SSRLoadedRenderer | undefined;
if (metadata.hydrate !== 'only') {
diff --git a/packages/astro/src/vite-plugin-html/index.ts b/packages/astro/src/vite-plugin-html/index.ts
new file mode 100644
index 000000000..c593bc201
--- /dev/null
+++ b/packages/astro/src/vite-plugin-html/index.ts
@@ -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);
+ }
+ }
+}
diff --git a/packages/astro/src/vite-plugin-html/transform/escape.ts b/packages/astro/src/vite-plugin-html/transform/escape.ts
new file mode 100644
index 000000000..8b5805614
--- /dev/null
+++ b/packages/astro/src/vite-plugin-html/transform/escape.ts
@@ -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;
diff --git a/packages/astro/src/vite-plugin-html/transform/index.ts b/packages/astro/src/vite-plugin-html/transform/index.ts
new file mode 100644
index 000000000..de6431ea7
--- /dev/null
+++ b/packages/astro/src/vite-plugin-html/transform/index.ts
@@ -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()
+ }
+}
diff --git a/packages/astro/src/vite-plugin-html/transform/slots.ts b/packages/astro/src/vite-plugin-html/transform/slots.ts
new file mode 100644
index 000000000..c8cb32f13
--- /dev/null
+++ b/packages/astro/src/vite-plugin-html/transform/slots.ts
@@ -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___`;
diff --git a/packages/astro/src/vite-plugin-html/transform/utils.ts b/packages/astro/src/vite-plugin-html/transform/utils.ts
new file mode 100644
index 000000000..313bfe662
--- /dev/null
+++ b/packages/astro/src/vite-plugin-html/transform/utils.ts
@@ -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, '\\${');
+}
diff --git a/packages/astro/test/fixtures/html-component/package.json b/packages/astro/test/fixtures/html-component/package.json
new file mode 100644
index 000000000..548f0bc7b
--- /dev/null
+++ b/packages/astro/test/fixtures/html-component/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/html-component",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/astro/test/fixtures/html-component/src/components/Test.html b/packages/astro/test/fixtures/html-component/src/components/Test.html
new file mode 100644
index 000000000..ec16bf314
--- /dev/null
+++ b/packages/astro/test/fixtures/html-component/src/components/Test.html
@@ -0,0 +1,3 @@
+<h1>Hello component!</h1>
+
+<div id="foo">bar</div>
diff --git a/packages/astro/test/fixtures/html-component/src/pages/index.astro b/packages/astro/test/fixtures/html-component/src/pages/index.astro
new file mode 100644
index 000000000..821d12538
--- /dev/null
+++ b/packages/astro/test/fixtures/html-component/src/pages/index.astro
@@ -0,0 +1,5 @@
+---
+import Test from '../components/Test.html';
+---
+
+<Test />
diff --git a/packages/astro/test/fixtures/html-escape/package.json b/packages/astro/test/fixtures/html-escape/package.json
new file mode 100644
index 000000000..b6eb74765
--- /dev/null
+++ b/packages/astro/test/fixtures/html-escape/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/html-escape",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/astro/test/fixtures/html-escape/src/components/Test.html b/packages/astro/test/fixtures/html-escape/src/components/Test.html
new file mode 100644
index 000000000..6ee4b5200
--- /dev/null
+++ b/packages/astro/test/fixtures/html-escape/src/components/Test.html
@@ -0,0 +1,4 @@
+<div>${foo}</div>
+<span ${attr}></span>
+<custom-element x-data="`${test}`"></custom-element>
+<script>console.log(`hello ${"world"}!`)</script>
diff --git a/packages/astro/test/fixtures/html-escape/src/pages/index.astro b/packages/astro/test/fixtures/html-escape/src/pages/index.astro
new file mode 100644
index 000000000..821d12538
--- /dev/null
+++ b/packages/astro/test/fixtures/html-escape/src/pages/index.astro
@@ -0,0 +1,5 @@
+---
+import Test from '../components/Test.html';
+---
+
+<Test />
diff --git a/packages/astro/test/fixtures/html-page/package.json b/packages/astro/test/fixtures/html-page/package.json
new file mode 100644
index 000000000..070024b1e
--- /dev/null
+++ b/packages/astro/test/fixtures/html-page/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/html-page",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/astro/test/fixtures/html-page/src/pages/index.html b/packages/astro/test/fixtures/html-page/src/pages/index.html
new file mode 100644
index 000000000..5d4d22827
--- /dev/null
+++ b/packages/astro/test/fixtures/html-page/src/pages/index.html
@@ -0,0 +1 @@
+<h1>Hello page!</h1>
diff --git a/packages/astro/test/fixtures/html-slots/package.json b/packages/astro/test/fixtures/html-slots/package.json
new file mode 100644
index 000000000..10b04cfd4
--- /dev/null
+++ b/packages/astro/test/fixtures/html-slots/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/html-slots",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/astro/test/fixtures/html-slots/src/components/Default.html b/packages/astro/test/fixtures/html-slots/src/components/Default.html
new file mode 100644
index 000000000..9f012095f
--- /dev/null
+++ b/packages/astro/test/fixtures/html-slots/src/components/Default.html
@@ -0,0 +1 @@
+<div id="default"><slot></slot></div>
diff --git a/packages/astro/test/fixtures/html-slots/src/components/Inline.html b/packages/astro/test/fixtures/html-slots/src/components/Inline.html
new file mode 100644
index 000000000..121164e38
--- /dev/null
+++ b/packages/astro/test/fixtures/html-slots/src/components/Inline.html
@@ -0,0 +1 @@
+<div id="inline"><slot is:inline></slot></div>
diff --git a/packages/astro/test/fixtures/html-slots/src/components/Named.html b/packages/astro/test/fixtures/html-slots/src/components/Named.html
new file mode 100644
index 000000000..0993cfb27
--- /dev/null
+++ b/packages/astro/test/fixtures/html-slots/src/components/Named.html
@@ -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>
diff --git a/packages/astro/test/fixtures/html-slots/src/pages/index.astro b/packages/astro/test/fixtures/html-slots/src/pages/index.astro
new file mode 100644
index 000000000..aa4a3bd25
--- /dev/null
+++ b/packages/astro/test/fixtures/html-slots/src/pages/index.astro
@@ -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>
diff --git a/packages/astro/test/html-component.test.js b/packages/astro/test/html-component.test.js
new file mode 100644
index 000000000..0145a22aa
--- /dev/null
+++ b/packages/astro/test/html-component.test.js
@@ -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');
+ });
+ });
+});
diff --git a/packages/astro/test/html-escape.test.js b/packages/astro/test/html-escape.test.js
new file mode 100644
index 000000000..ed19105c6
--- /dev/null
+++ b/packages/astro/test/html-escape.test.js
@@ -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"}!`)');
+ });
+ });
+});
diff --git a/packages/astro/test/html-page.test.js b/packages/astro/test/html-page.test.js
new file mode 100644
index 000000000..fe4c01f68
--- /dev/null
+++ b/packages/astro/test/html-page.test.js
@@ -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!');
+ });
+ });
+});
diff --git a/packages/astro/test/html-slots.test.js b/packages/astro/test/html-slots.test.js
new file mode 100644
index 000000000..af1a05a83
--- /dev/null
+++ b/packages/astro/test/html-slots.test.js
@@ -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>');
+ });
+ });
+});
diff --git a/packages/webapi/mod.d.ts b/packages/webapi/mod.d.ts
index a3c49dc5c..7150edbe7 100644
--- a/packages/webapi/mod.d.ts
+++ b/packages/webapi/mod.d.ts
@@ -9,4 +9,4 @@ interface PolyfillOptions {
override?: Record<string, {
(...args: any[]): any;
}>;
-} \ No newline at end of file
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e7816eb5b..bd34408fe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -506,6 +506,7 @@ importers:
prismjs: ^1.28.0
prompts: ^2.4.2
recast: ^0.20.5
+ rehype: ^12.0.1
resolve: ^1.22.0
rollup: ^2.75.6
sass: ^1.52.2
@@ -519,6 +520,8 @@ importers:
strip-ansi: ^7.0.1
supports-esm: ^1.0.0
tsconfig-resolver: ^3.0.1
+ unist-util-visit: ^4.1.0
+ vfile: ^5.3.2
vite: 3.0.2
yargs-parser: ^21.0.1
zod: ^3.17.3
@@ -566,6 +569,7 @@ importers:
prismjs: 1.28.0
prompts: 2.4.2
recast: 0.20.5
+ rehype: 12.0.1
resolve: 1.22.1
rollup: 2.76.0
semver: 7.3.7
@@ -577,6 +581,8 @@ importers:
strip-ansi: 7.0.1
supports-esm: 1.0.0
tsconfig-resolver: 3.0.1
+ unist-util-visit: 4.1.0
+ vfile: 5.3.4
vite: 3.0.2_sass@1.53.0
yargs-parser: 21.0.1
zod: 3.17.3
@@ -1526,6 +1532,30 @@ importers:
dependencies:
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:
specifiers:
astro: workspace:*
@@ -9547,7 +9577,7 @@ packages:
dev: true
/concat-map/0.0.1:
- resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
/concurrently/7.2.2:
resolution: {integrity: sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw==}
@@ -14217,6 +14247,15 @@ packages:
unist-util-visit: 4.1.0
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:
resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==}
dependencies:
@@ -14251,6 +14290,15 @@ packages:
'@jsdevtools/rehype-toc': 3.0.2
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:
resolution: {integrity: sha512-KsHQbaI4FX8Ozxqk7YErxwmBiveUqloKuVqyPG2YPLHojpgomodWgRfG4B+bOtmn/5bfJ8khw4rR0lvgVFl2Uw==}
dependencies: