Resolve components by module ID during compilation (#3300)
* WIP: adding test coverage * test fixes * moving the shared lib up a directory to reproduce the bug * fix: transform with the module ID instead of parsing the filepath * adding the shared lib to the workspaces list * fix: client-only assets now get the full URL from vite * why is this needed for windows? * WIP: using /@fs to handle windows filepaths * fix: remove /@fs from hoisted script imports * nit: removing unused imports * fix: strip off the path root when mapping client:only styles * had to reverse the `/@fs` handling to work on windows and unix * chore: adding comments to explain the fix * chore: adding changeset
This commit is contained in:
parent
2fed346a00
commit
b463ddb3ce
22 changed files with 407 additions and 41 deletions
5
.changeset/proud-cups-brush.md
Normal file
5
.changeset/proud-cups-brush.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Resolve .astro components by module ID to support the use of Astro + framework components in an NPM package
|
|
@ -33,8 +33,7 @@
|
||||||
"examples/component/packages/*",
|
"examples/component/packages/*",
|
||||||
"scripts",
|
"scripts",
|
||||||
"smoke/*",
|
"smoke/*",
|
||||||
"packages/astro/test/fixtures/builtins/packages/*",
|
"packages/astro/test/fixtures/component-library-shared",
|
||||||
"packages/astro/test/fixtures/builtins-polyfillnode",
|
|
||||||
"packages/astro/test/fixtures/custom-elements/my-component-lib",
|
"packages/astro/test/fixtures/custom-elements/my-component-lib",
|
||||||
"packages/astro/test/fixtures/static build/pkg"
|
"packages/astro/test/fixtures/static build/pkg"
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import type { AstroConfig, RouteData } from '../../@types/astro';
|
|
||||||
import type { RenderedChunk } from 'rollup';
|
import type { RenderedChunk } from 'rollup';
|
||||||
import type { PageBuildData, ViteID } from './types';
|
import type { PageBuildData, ViteID } from './types';
|
||||||
|
|
||||||
import { fileURLToPath } from 'url';
|
import { prependForwardSlash } from '../path.js';
|
||||||
import { viteID } from '../util.js';
|
import { viteID } from '../util.js';
|
||||||
|
|
||||||
export interface BuildInternals {
|
export interface BuildInternals {
|
||||||
|
@ -80,17 +79,16 @@ export function trackPageData(
|
||||||
export function trackClientOnlyPageDatas(
|
export function trackClientOnlyPageDatas(
|
||||||
internals: BuildInternals,
|
internals: BuildInternals,
|
||||||
pageData: PageBuildData,
|
pageData: PageBuildData,
|
||||||
clientOnlys: string[],
|
clientOnlys: string[]
|
||||||
astroConfig: AstroConfig
|
|
||||||
) {
|
) {
|
||||||
for (const clientOnlyComponent of clientOnlys) {
|
for (const clientOnlyComponent of clientOnlys) {
|
||||||
const coPath = viteID(new URL('.' + clientOnlyComponent, astroConfig.root));
|
|
||||||
let pageDataSet: Set<PageBuildData>;
|
let pageDataSet: Set<PageBuildData>;
|
||||||
if (internals.pagesByClientOnly.has(coPath)) {
|
// clientOnlyComponent will be similar to `/@fs{moduleID}`
|
||||||
pageDataSet = internals.pagesByClientOnly.get(coPath)!;
|
if (internals.pagesByClientOnly.has(clientOnlyComponent)) {
|
||||||
|
pageDataSet = internals.pagesByClientOnly.get(clientOnlyComponent)!;
|
||||||
} else {
|
} else {
|
||||||
pageDataSet = new Set<PageBuildData>();
|
pageDataSet = new Set<PageBuildData>();
|
||||||
internals.pagesByClientOnly.set(coPath, pageDataSet);
|
internals.pagesByClientOnly.set(clientOnlyComponent, pageDataSet);
|
||||||
}
|
}
|
||||||
pageDataSet.add(pageData);
|
pageDataSet.add(pageData);
|
||||||
}
|
}
|
||||||
|
@ -115,8 +113,10 @@ export function* getPageDatasByClientOnlyChunk(
|
||||||
const pagesByClientOnly = internals.pagesByClientOnly;
|
const pagesByClientOnly = internals.pagesByClientOnly;
|
||||||
if (pagesByClientOnly.size) {
|
if (pagesByClientOnly.size) {
|
||||||
for (const [modulePath] of Object.entries(chunk.modules)) {
|
for (const [modulePath] of Object.entries(chunk.modules)) {
|
||||||
if (pagesByClientOnly.has(modulePath)) {
|
// prepend with `/@fs` to match the path used in the compiler's transform() call
|
||||||
for (const pageData of pagesByClientOnly.get(modulePath)!) {
|
const pathname = `/@fs${prependForwardSlash(modulePath)}`;
|
||||||
|
if (pagesByClientOnly.has(pathname)) {
|
||||||
|
for (const pageData of pagesByClientOnly.get(pathname)!) {
|
||||||
yield pageData;
|
yield pageData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
|
||||||
|
|
||||||
// Track client:only usage so we can map their CSS back to the Page they are used in.
|
// Track client:only usage so we can map their CSS back to the Page they are used in.
|
||||||
const clientOnlys = Array.from(metadata.clientOnlyComponentPaths());
|
const clientOnlys = Array.from(metadata.clientOnlyComponentPaths());
|
||||||
trackClientOnlyPageDatas(internals, pageData, clientOnlys, astroConfig);
|
trackClientOnlyPageDatas(internals, pageData, clientOnlys);
|
||||||
|
|
||||||
const topLevelImports = new Set([
|
const topLevelImports = new Set([
|
||||||
// Any component that gets hydrated
|
// Any component that gets hydrated
|
||||||
|
|
|
@ -25,7 +25,12 @@ export function vitePluginHoistedScripts(
|
||||||
if (virtualHoistedEntry(id)) {
|
if (virtualHoistedEntry(id)) {
|
||||||
let code = '';
|
let code = '';
|
||||||
for (let path of internals.hoistedScriptIdToHoistedMap.get(id)!) {
|
for (let path of internals.hoistedScriptIdToHoistedMap.get(id)!) {
|
||||||
code += `import "${path}";`;
|
let importPath = path;
|
||||||
|
// `/@fs` is added during the compiler's transform() step
|
||||||
|
if (importPath.startsWith('/@fs')) {
|
||||||
|
importPath = importPath.slice('/@fs'.length);
|
||||||
|
}
|
||||||
|
code += `import "${importPath}";`;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
|
|
|
@ -34,16 +34,25 @@ function safelyReplaceImportPlaceholder(code: string) {
|
||||||
|
|
||||||
const configCache = new WeakMap<AstroConfig, CompilationCache>();
|
const configCache = new WeakMap<AstroConfig, CompilationCache>();
|
||||||
|
|
||||||
async function compile(
|
interface CompileProps {
|
||||||
config: AstroConfig,
|
config: AstroConfig;
|
||||||
filename: string,
|
filename: string;
|
||||||
source: string,
|
moduleId: string;
|
||||||
viteTransform: TransformHook,
|
source: string;
|
||||||
opts: { ssr: boolean }
|
ssr: boolean;
|
||||||
): Promise<CompileResult> {
|
viteTransform: TransformHook;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compile({
|
||||||
|
config,
|
||||||
|
filename,
|
||||||
|
moduleId,
|
||||||
|
source,
|
||||||
|
ssr,
|
||||||
|
viteTransform,
|
||||||
|
}: CompileProps): Promise<CompileResult> {
|
||||||
const filenameURL = new URL(`file://${filename}`);
|
const filenameURL = new URL(`file://${filename}`);
|
||||||
const normalizedID = fileURLToPath(filenameURL);
|
const normalizedID = fileURLToPath(filenameURL);
|
||||||
const pathname = filenameURL.pathname.slice(config.root.pathname.length - 1);
|
|
||||||
|
|
||||||
let rawCSSDeps = new Set<string>();
|
let rawCSSDeps = new Set<string>();
|
||||||
let cssTransformError: Error | undefined;
|
let cssTransformError: Error | undefined;
|
||||||
|
@ -52,7 +61,8 @@ async function compile(
|
||||||
// use `sourcemap: "both"` so that sourcemap is included in the code
|
// use `sourcemap: "both"` so that sourcemap is included in the code
|
||||||
// result passed to esbuild, but also available in the catch handler.
|
// result passed to esbuild, but also available in the catch handler.
|
||||||
const transformResult = await transform(source, {
|
const transformResult = await transform(source, {
|
||||||
pathname,
|
// For Windows compat, prepend the module ID with `/@fs`
|
||||||
|
pathname: `/@fs${prependForwardSlash(moduleId)}`,
|
||||||
projectRoot: config.root.toString(),
|
projectRoot: config.root.toString(),
|
||||||
site: config.site ? new URL(config.base, config.site).toString() : undefined,
|
site: config.site ? new URL(config.base, config.site).toString() : undefined,
|
||||||
sourcefile: filename,
|
sourcefile: filename,
|
||||||
|
@ -86,7 +96,7 @@ async function compile(
|
||||||
lang,
|
lang,
|
||||||
id: normalizedID,
|
id: normalizedID,
|
||||||
transformHook: viteTransform,
|
transformHook: viteTransform,
|
||||||
ssr: opts.ssr,
|
ssr,
|
||||||
});
|
});
|
||||||
|
|
||||||
let map: SourceMapInput | undefined;
|
let map: SourceMapInput | undefined;
|
||||||
|
@ -131,13 +141,8 @@ export function invalidateCompilation(config: AstroConfig, filename: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cachedCompilation(
|
export async function cachedCompilation(props: CompileProps): Promise<CompileResult> {
|
||||||
config: AstroConfig,
|
const { config, filename } = props;
|
||||||
filename: string,
|
|
||||||
source: string,
|
|
||||||
viteTransform: TransformHook,
|
|
||||||
opts: { ssr: boolean }
|
|
||||||
): Promise<CompileResult> {
|
|
||||||
let cache: CompilationCache;
|
let cache: CompilationCache;
|
||||||
if (!configCache.has(config)) {
|
if (!configCache.has(config)) {
|
||||||
cache = new Map();
|
cache = new Map();
|
||||||
|
@ -148,7 +153,7 @@ export async function cachedCompilation(
|
||||||
if (cache.has(filename)) {
|
if (cache.has(filename)) {
|
||||||
return cache.get(filename)!;
|
return cache.get(filename)!;
|
||||||
}
|
}
|
||||||
const compileResult = await compile(config, filename, source, viteTransform, opts);
|
const compileResult = await compile(props);
|
||||||
cache.set(filename, compileResult);
|
cache.set(filename, compileResult);
|
||||||
return compileResult;
|
return compileResult;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,15 +99,21 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu
|
||||||
if (isPage && config._ctx.scripts.some((s) => s.stage === 'page')) {
|
if (isPage && config._ctx.scripts.some((s) => s.stage === 'page')) {
|
||||||
source += `\n<script src="${PAGE_SCRIPT_ID}" />`;
|
source += `\n<script src="${PAGE_SCRIPT_ID}" />`;
|
||||||
}
|
}
|
||||||
|
const compileProps = {
|
||||||
|
config,
|
||||||
|
filename,
|
||||||
|
moduleId: id,
|
||||||
|
source,
|
||||||
|
ssr: Boolean(opts?.ssr),
|
||||||
|
viteTransform,
|
||||||
|
};
|
||||||
if (query.astro) {
|
if (query.astro) {
|
||||||
if (query.type === 'style') {
|
if (query.type === 'style') {
|
||||||
if (typeof query.index === 'undefined') {
|
if (typeof query.index === 'undefined') {
|
||||||
throw new Error(`Requests for Astro CSS must include an index.`);
|
throw new Error(`Requests for Astro CSS must include an index.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const transformResult = await cachedCompilation(config, filename, source, viteTransform, {
|
const transformResult = await cachedCompilation(compileProps);
|
||||||
ssr: Boolean(opts?.ssr),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Track any CSS dependencies so that HMR is triggered when they change.
|
// Track any CSS dependencies so that HMR is triggered when they change.
|
||||||
await trackCSSDependencies.call(this, {
|
await trackCSSDependencies.call(this, {
|
||||||
|
@ -133,9 +139,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const transformResult = await cachedCompilation(config, filename, source, viteTransform, {
|
const transformResult = await cachedCompilation(compileProps);
|
||||||
ssr: Boolean(opts?.ssr),
|
|
||||||
});
|
|
||||||
const scripts = transformResult.scripts;
|
const scripts = transformResult.scripts;
|
||||||
const hoistedScript = scripts[query.index];
|
const hoistedScript = scripts[query.index];
|
||||||
|
|
||||||
|
@ -163,9 +167,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const transformResult = await cachedCompilation(config, filename, source, viteTransform, {
|
const transformResult = await cachedCompilation(compileProps);
|
||||||
ssr: Boolean(opts?.ssr),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Compile all TypeScript to JavaScript.
|
// Compile all TypeScript to JavaScript.
|
||||||
// Also, catches invalid JS/TS in the compiled output before returning.
|
// Also, catches invalid JS/TS in the compiled output before returning.
|
||||||
|
|
157
packages/astro/test/component-library.test.js
Normal file
157
packages/astro/test/component-library.test.js
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { load as cheerioLoad } from 'cheerio';
|
||||||
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
function addLeadingSlash(path) {
|
||||||
|
return path.startsWith('/') ? path : '/' + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Component Libraries', () => {
|
||||||
|
/** @type {import('./test-utils').Fixture} */
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/component-library/',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('build', async () => {
|
||||||
|
before(async () => {
|
||||||
|
await fixture.build();
|
||||||
|
});
|
||||||
|
|
||||||
|
function createFindEvidence(expected, prefix) {
|
||||||
|
return async function findEvidence(pathname) {
|
||||||
|
const html = await fixture.readFile(pathname);
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
const links = $('link[rel=stylesheet]');
|
||||||
|
for (const link of links) {
|
||||||
|
const href = $(link).attr('href');
|
||||||
|
|
||||||
|
const data = await fixture.readFile(addLeadingSlash(href));
|
||||||
|
if (expected.test(data)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
it('Built .astro pages', async () => {
|
||||||
|
let html = await fixture.readFile('/with-astro/index.html');
|
||||||
|
expect(html).to.be.a('string');
|
||||||
|
|
||||||
|
html = await fixture.readFile('/with-react/index.html');
|
||||||
|
expect(html).to.be.a('string');
|
||||||
|
|
||||||
|
html = await fixture.readFile('/internal-hydration/index.html');
|
||||||
|
expect(html).to.be.a('string');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Works with .astro components', async () => {
|
||||||
|
const html = await fixture.readFile('/with-astro/index.html');
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
|
||||||
|
expect($('button').text()).to.equal('Click me', "Rendered the component's slot");
|
||||||
|
|
||||||
|
const findEvidence = createFindEvidence(/border-radius:( )*1rem/);
|
||||||
|
expect(await findEvidence('with-astro/index.html')).to.equal(
|
||||||
|
true,
|
||||||
|
"Included the .astro component's <style>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Works with react components', async () => {
|
||||||
|
const html = await fixture.readFile('/with-react/index.html');
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
|
||||||
|
expect($('#react-static').text()).to.equal('Hello static!', 'Rendered the static component');
|
||||||
|
|
||||||
|
expect($('#react-idle').text()).to.equal(
|
||||||
|
'Hello idle!',
|
||||||
|
'Rendered the client hydrated component'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($('astro-root[uid]')).to.have.lengthOf(1, 'Included one hydration island');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Works with components hydrated internally', async () => {
|
||||||
|
const html = await fixture.readFile('/internal-hydration/index.html');
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
|
||||||
|
expect($('.counter').length).to.equal(1, 'Rendered the svelte counter');
|
||||||
|
expect($('.counter-message').text().trim()).to.equal('Hello, Svelte!', "rendered the counter's slot");
|
||||||
|
|
||||||
|
expect($('astro-root[uid]')).to.have.lengthOf(1, 'Included one hydration island');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dev', async () => {
|
||||||
|
let devServer;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
devServer = await fixture.startDevServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await devServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
function createFindEvidence(expected, prefix) {
|
||||||
|
return async function findEvidence(pathname) {
|
||||||
|
const html = await fixture.fetch(pathname).then((res) => res.text());
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
const links = $('link[rel=stylesheet]');
|
||||||
|
for (const link of links) {
|
||||||
|
const href = $(link).attr('href');
|
||||||
|
|
||||||
|
const data = await fixture.fetch(addLeadingSlash(href)).then((res) => res.text());
|
||||||
|
if (expected.test(data)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
it('Works with .astro components', async () => {
|
||||||
|
const html = await fixture.fetch('/with-astro/').then((res) => res.text());
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
|
||||||
|
expect($('button').text()).to.equal('Click me', "Rendered the component's slot");
|
||||||
|
|
||||||
|
const findEvidence = createFindEvidence(/border-radius:( )*1rem/);
|
||||||
|
expect(await findEvidence('/with-astro/')).to.equal(
|
||||||
|
true,
|
||||||
|
"Included the .astro component's <style>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Works with react components', async () => {
|
||||||
|
const html = await fixture.fetch('/with-react/').then((res) => res.text());
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
|
||||||
|
expect($('#react-static').text()).to.equal('Hello static!', 'Rendered the static component');
|
||||||
|
|
||||||
|
expect($('#react-idle').text()).to.equal(
|
||||||
|
'Hello idle!',
|
||||||
|
'Rendered the client hydrated component'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($('astro-root[uid]')).to.have.lengthOf(1, 'Included one hydration island');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Works with components hydrated internally', async () => {
|
||||||
|
const html = await fixture.fetch('/internal-hydration/').then((res) => res.text());
|
||||||
|
const $ = cheerioLoad(html);
|
||||||
|
|
||||||
|
expect($('.counter').length).to.equal(1, 'Rendered the svelte counter');
|
||||||
|
expect($('.counter-message').text().trim()).to.equal('Hello, Svelte!', "rendered the counter's slot");
|
||||||
|
|
||||||
|
expect($('astro-root[uid]')).to.have.lengthOf(1, 'Included one hydration island');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
7
packages/astro/test/fixtures/component-library-shared/Button.astro
vendored
Normal file
7
packages/astro/test/fixtures/component-library-shared/Button.astro
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<button><slot /></button>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
button {
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
11
packages/astro/test/fixtures/component-library-shared/Counter.css
vendored
Normal file
11
packages/astro/test/fixtures/component-library-shared/Counter.css
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.counter {
|
||||||
|
display: grid;
|
||||||
|
font-size: 2em;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
margin-top: 2em;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter-message {
|
||||||
|
text-align: center;
|
||||||
|
}
|
20
packages/astro/test/fixtures/component-library-shared/Counter.jsx
vendored
Normal file
20
packages/astro/test/fixtures/component-library-shared/Counter.jsx
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { h } from 'preact';
|
||||||
|
import { useState } from 'preact/hooks';
|
||||||
|
import './Counter.css';
|
||||||
|
|
||||||
|
export default function Counter({ children }) {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
const add = () => setCount((i) => i + 1);
|
||||||
|
const subtract = () => setCount((i) => i - 1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div class="counter">
|
||||||
|
<button onClick={subtract}>-</button>
|
||||||
|
<pre>{count}</pre>
|
||||||
|
<button onClick={add}>+</button>
|
||||||
|
</div>
|
||||||
|
<div class="counter-message">{children}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
33
packages/astro/test/fixtures/component-library-shared/Counter.svelte
vendored
Normal file
33
packages/astro/test/fixtures/component-library-shared/Counter.svelte
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<script>
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function add() {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function subtract() {
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="counter">
|
||||||
|
<button on:click={subtract}>-</button>
|
||||||
|
<pre>{ count }</pre>
|
||||||
|
<button on:click={add}>+</button>
|
||||||
|
</div>
|
||||||
|
<div class="message">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.counter{
|
||||||
|
display: grid;
|
||||||
|
font-size: 2em;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
margin-top: 2em;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
7
packages/astro/test/fixtures/component-library-shared/CounterWrapper.astro
vendored
Normal file
7
packages/astro/test/fixtures/component-library-shared/CounterWrapper.astro
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
import Counter from './Counter.jsx'
|
||||||
|
---
|
||||||
|
|
||||||
|
<Counter client:visible>
|
||||||
|
<slot />
|
||||||
|
</Counter>
|
5
packages/astro/test/fixtures/component-library-shared/HelloReact.jsx
vendored
Normal file
5
packages/astro/test/fixtures/component-library-shared/HelloReact.jsx
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function ({ name, unused }) {
|
||||||
|
return <h2 id={`react-${name}`}>Hello {name}!</h2>;
|
||||||
|
}
|
5
packages/astro/test/fixtures/component-library-shared/README.md
vendored
Normal file
5
packages/astro/test/fixtures/component-library-shared/README.md
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Shared Component Library
|
||||||
|
|
||||||
|
> This packages mimics a component library that would be published to NPM
|
||||||
|
|
||||||
|
This used by the `component-library` test fixture to make sure that `.astro` component are resolved properly when they live outside the project's root directory.
|
20
packages/astro/test/fixtures/component-library-shared/package.json
vendored
Normal file
20
packages/astro/test/fixtures/component-library-shared/package.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "@test/component-library-shared",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
"./Button.astro": "./Button.astro",
|
||||||
|
"./CounterWrapper": "./CounterWrapper.astro",
|
||||||
|
"./HelloReact": "./HelloReact.jsx"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"Button.astro",
|
||||||
|
"CounterWrapper.astro",
|
||||||
|
"Counter.svelte",
|
||||||
|
"HelloReact.jsx"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
8
packages/astro/test/fixtures/component-library/astro.config.mjs
vendored
Normal file
8
packages/astro/test/fixtures/component-library/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import preact from '@astrojs/preact';
|
||||||
|
import react from '@astrojs/react';
|
||||||
|
import svelte from '@astrojs/svelte';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [preact(), react(), svelte()],
|
||||||
|
})
|
14
packages/astro/test/fixtures/component-library/package.json
vendored
Normal file
14
packages/astro/test/fixtures/component-library/package.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "@test/component-library",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*",
|
||||||
|
"@astrojs/preact": "workspace:*",
|
||||||
|
"@astrojs/react": "workspace:*",
|
||||||
|
"@astrojs/svelte": "workspace:*",
|
||||||
|
"@test/component-library-shared": "workspace:*",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
}
|
||||||
|
}
|
17
packages/astro/test/fixtures/component-library/src/pages/internal-hydration.astro
vendored
Normal file
17
packages/astro/test/fixtures/component-library/src/pages/internal-hydration.astro
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
import CounterWrapper from '@test/component-library-shared/CounterWrapper';
|
||||||
|
const title = 'With Astro Component';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{title}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>{title}</h1>
|
||||||
|
|
||||||
|
<CounterWrapper>
|
||||||
|
<h1>Hello, Svelte!</h1>
|
||||||
|
</CounterWrapper>
|
||||||
|
</body>
|
||||||
|
</html>
|
15
packages/astro/test/fixtures/component-library/src/pages/with-astro.astro
vendored
Normal file
15
packages/astro/test/fixtures/component-library/src/pages/with-astro.astro
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
import Button from '@test/component-library-shared/Button.astro';
|
||||||
|
const title = 'With Astro Component';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{title}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>{title}</h1>
|
||||||
|
|
||||||
|
<Button>Click me</Button>
|
||||||
|
</body>
|
||||||
|
</html>
|
7
packages/astro/test/fixtures/component-library/src/pages/with-react.astro
vendored
Normal file
7
packages/astro/test/fixtures/component-library/src/pages/with-react.astro
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
import HelloReact from '@test/component-library-shared/HelloReact';
|
||||||
|
---
|
||||||
|
|
||||||
|
<HelloReact name="static" />
|
||||||
|
|
||||||
|
<HelloReact name="idle" client:idle />
|
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
|
@ -951,6 +951,30 @@ importers:
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/component-library:
|
||||||
|
specifiers:
|
||||||
|
'@astrojs/preact': workspace:*
|
||||||
|
'@astrojs/react': workspace:*
|
||||||
|
'@astrojs/svelte': workspace:*
|
||||||
|
'@test/component-library-shared': workspace:*
|
||||||
|
astro: workspace:*
|
||||||
|
react: ^18.0.0
|
||||||
|
react-dom: ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
'@astrojs/preact': link:../../../../integrations/preact
|
||||||
|
'@astrojs/react': link:../../../../integrations/react
|
||||||
|
'@astrojs/svelte': link:../../../../integrations/svelte
|
||||||
|
'@test/component-library-shared': link:../component-library-shared
|
||||||
|
astro: link:../../..
|
||||||
|
react: 18.0.0
|
||||||
|
react-dom: 18.0.0_react@18.0.0
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/component-library-shared:
|
||||||
|
specifiers:
|
||||||
|
astro: workspace:*
|
||||||
|
devDependencies:
|
||||||
|
astro: link:../../..
|
||||||
|
|
||||||
packages/astro/test/fixtures/config-host:
|
packages/astro/test/fixtures/config-host:
|
||||||
specifiers:
|
specifiers:
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
|
|
Loading…
Add table
Reference in a new issue