Replace class:list
implementation with clsx
(#8142)
* chore: replace `class:list` implementation with `clsx` * chore: remove Set support from `class:list` test * chore: better class:list test * Update packages/astro/src/runtime/server/render/component.ts
This commit is contained in:
parent
5e6bd6ab5d
commit
81545197a3
12 changed files with 133 additions and 68 deletions
11
.changeset/cool-jokes-clap.md
Normal file
11
.changeset/cool-jokes-clap.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
'astro': major
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes for the `class:list` directive
|
||||||
|
|
||||||
|
- Previously, `class:list` would ocassionally not be merged the `class` prop when passed to Astro components. Now, `class:list` is always converted to a `class` prop (as a string value).
|
||||||
|
- Previously, `class:list` diverged from [`clsx`](https://github.com/lukeed/clsx) in a few edge cases. Now, `class:list` uses [`clsx`](https://github.com/lukeed/clsx) directly.
|
||||||
|
- `class:list` used to deduplicate matching values, but it no longer does
|
||||||
|
- `class:list` used to sort individual values, but it no longer does
|
||||||
|
- `class:list` used to support `Set` and other iterables, but it no longer does
|
|
@ -134,6 +134,7 @@
|
||||||
"boxen": "^6.2.1",
|
"boxen": "^6.2.1",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"ci-info": "^3.8.0",
|
"ci-info": "^3.8.0",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
"common-ancestor-path": "^1.0.1",
|
"common-ancestor-path": "^1.0.1",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
|
|
@ -7,7 +7,6 @@ import type {
|
||||||
import { AstroError, AstroErrorData } from '../../core/errors/index.js';
|
import { AstroError, AstroErrorData } from '../../core/errors/index.js';
|
||||||
import { escapeHTML } from './escape.js';
|
import { escapeHTML } from './escape.js';
|
||||||
import { serializeProps } from './serialize.js';
|
import { serializeProps } from './serialize.js';
|
||||||
import { serializeListValue } from './util.js';
|
|
||||||
|
|
||||||
export interface HydrationMetadata {
|
export interface HydrationMetadata {
|
||||||
directive: string;
|
directive: string;
|
||||||
|
@ -95,11 +94,6 @@ export function extractDirectives(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (key === 'class:list') {
|
|
||||||
if (value) {
|
|
||||||
// support "class" from an expression passed into a component (#782)
|
|
||||||
extracted.props[key.slice(0, -5)] = serializeListValue(value);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
extracted.props[key] = value;
|
extracted.props[key] = value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import type {
|
||||||
} from '../../../@types/astro';
|
} from '../../../@types/astro';
|
||||||
import type { RenderInstruction } from './types.js';
|
import type { RenderInstruction } from './types.js';
|
||||||
|
|
||||||
|
import { clsx } from 'clsx';
|
||||||
import { AstroError, AstroErrorData } from '../../../core/errors/index.js';
|
import { AstroError, AstroErrorData } from '../../../core/errors/index.js';
|
||||||
import { HTMLBytes, markHTMLString } from '../escape.js';
|
import { HTMLBytes, markHTMLString } from '../escape.js';
|
||||||
import { extractDirectives, generateHydrateScript } from '../hydration.js';
|
import { extractDirectives, generateHydrateScript } from '../hydration.js';
|
||||||
|
@ -461,6 +462,9 @@ export async function renderComponent(
|
||||||
return await renderFragmentComponent(result, slots);
|
return await renderFragmentComponent(result, slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure directives (`class:list`) are processed
|
||||||
|
props = normalizeProps(props);
|
||||||
|
|
||||||
// .html components
|
// .html components
|
||||||
if (isHTMLComponent(Component)) {
|
if (isHTMLComponent(Component)) {
|
||||||
return await renderHTMLComponent(result, Component, props, slots);
|
return await renderHTMLComponent(result, Component, props, slots);
|
||||||
|
@ -473,6 +477,18 @@ export async function renderComponent(
|
||||||
return await renderFrameworkComponent(result, displayName, Component, props, slots);
|
return await renderFrameworkComponent(result, displayName, Component, props, slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeProps(props: Record<string, any>): Record<string, any> {
|
||||||
|
if (props['class:list'] !== undefined) {
|
||||||
|
const value = props['class:list'];
|
||||||
|
delete props['class:list'];
|
||||||
|
props['class'] = clsx(props['class'], value)
|
||||||
|
if (props['class'] === '') {
|
||||||
|
delete props['class']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
export async function renderComponentToString(
|
export async function renderComponentToString(
|
||||||
result: SSRResult,
|
result: SSRResult,
|
||||||
displayName: string,
|
displayName: string,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { SSRElement } from '../../../@types/astro';
|
import type { SSRElement } from '../../../@types/astro';
|
||||||
|
|
||||||
import { HTMLString, markHTMLString } from '../escape.js';
|
import { HTMLString, markHTMLString } from '../escape.js';
|
||||||
import { serializeListValue } from '../util.js';
|
import { clsx } from 'clsx';
|
||||||
|
|
||||||
export const voidElementNames =
|
export const voidElementNames =
|
||||||
/^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
|
/^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
|
||||||
|
@ -78,7 +78,7 @@ Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the
|
||||||
|
|
||||||
// support "class" from an expression passed into an element (#782)
|
// support "class" from an expression passed into an element (#782)
|
||||||
if (key === 'class:list') {
|
if (key === 'class:list') {
|
||||||
const listValue = toAttributeString(serializeListValue(value), shouldEscape);
|
const listValue = toAttributeString(clsx(value), shouldEscape);
|
||||||
if (listValue === '') {
|
if (listValue === '') {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,3 @@
|
||||||
export function serializeListValue(value: any) {
|
|
||||||
const hash: Record<string, any> = {};
|
|
||||||
|
|
||||||
push(value);
|
|
||||||
|
|
||||||
return Object.keys(hash).join(' ');
|
|
||||||
|
|
||||||
function push(item: any) {
|
|
||||||
// push individual iteratables
|
|
||||||
if (item && typeof item.forEach === 'function') item.forEach(push);
|
|
||||||
// otherwise, push object value keys by truthiness
|
|
||||||
else if (item === Object(item))
|
|
||||||
Object.keys(item).forEach((name) => {
|
|
||||||
if (item[name]) push(name);
|
|
||||||
});
|
|
||||||
// otherwise, push any other values as a string
|
|
||||||
else {
|
|
||||||
// get the item as a string
|
|
||||||
item = item === false || item == null ? '' : String(item).trim();
|
|
||||||
|
|
||||||
// add the item if it is filled
|
|
||||||
if (item) {
|
|
||||||
item.split(/\s+/).forEach((name: string) => {
|
|
||||||
hash[name] = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isPromise<T = any>(value: any): value is Promise<T> {
|
export function isPromise<T = any>(value: any): value is Promise<T> {
|
||||||
return !!value && typeof value === 'object' && typeof value.then === 'function';
|
return !!value && typeof value === 'object' && typeof value.then === 'function';
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,14 @@ describe('Class List', async () => {
|
||||||
const html = await fixture.readFile('/index.html');
|
const html = await fixture.readFile('/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
expect($('[class="test control"]')).to.have.lengthOf(1);
|
expect($('[class="test control"]')).to.have.lengthOf(1, '[class="test control"]');
|
||||||
expect($('[class="test expression"]')).to.have.lengthOf(1);
|
expect($('[class="test expression"]')).to.have.lengthOf(1, '[class="test expression"]');
|
||||||
expect($('[class="test true"]')).to.have.lengthOf(1);
|
expect($('[class="test true"]')).to.have.lengthOf(1, '[class="test true"]');
|
||||||
expect($('[class="test truthy"]')).to.have.lengthOf(1);
|
expect($('[class="test truthy"]')).to.have.lengthOf(1, '[class="test truthy"]');
|
||||||
expect($('[class="test set"]')).to.have.lengthOf(1);
|
expect($('[class="test set"]')).to.have.lengthOf(1, '[class="test set"]');
|
||||||
expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
|
expect($('[class="hello goodbye hello world hello friend"]')).to.have.lengthOf(1, '[class="hello goodbye hello world hello friend"]');
|
||||||
expect($('[class="foo baz"]')).to.have.lengthOf(1);
|
expect($('[class="foo baz"]')).to.have.lengthOf(1, '[class="foo baz"]');
|
||||||
expect($('span:not([class])')).to.have.lengthOf(1);
|
expect($('span:not([class])')).to.have.lengthOf(1, 'span:not([class])');
|
||||||
|
|
||||||
expect($('.false, .noshow1, .noshow2, .noshow3, .noshow4')).to.have.lengthOf(0);
|
expect($('.false, .noshow1, .noshow2, .noshow3, .noshow4')).to.have.lengthOf(0);
|
||||||
});
|
});
|
||||||
|
@ -30,13 +30,22 @@ describe('Class List', async () => {
|
||||||
const html = await fixture.readFile('/component/index.html');
|
const html = await fixture.readFile('/component/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
expect($('[class="test control"]')).to.have.lengthOf(1);
|
expect($('[class="test control"]')).to.have.lengthOf(1, '[class="test control"]');
|
||||||
expect($('[class="test expression"]')).to.have.lengthOf(1);
|
expect($('[class="test expression"]')).to.have.lengthOf(1, '[class="test expression"]');
|
||||||
expect($('[class="test true"]')).to.have.lengthOf(1);
|
expect($('[class="test true"]')).to.have.lengthOf(1, '[class="test true"]');
|
||||||
expect($('[class="test truthy"]')).to.have.lengthOf(1);
|
expect($('[class="test truthy"]')).to.have.lengthOf(1, '[class="test truthy"]');
|
||||||
expect($('[class="test set"]')).to.have.lengthOf(1);
|
expect($('[class="test set"]')).to.have.lengthOf(1, '[class="test set"]');
|
||||||
expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
|
expect($('[class="hello goodbye hello world hello friend"]')).to.have.lengthOf(1, '[class="hello goodbye hello world hello friend"]');
|
||||||
expect($('[class="foo baz"]')).to.have.lengthOf(1);
|
expect($('[class="foo baz"]')).to.have.lengthOf(1, '[class="foo baz"]');
|
||||||
expect($('span:not([class])')).to.have.lengthOf(1);
|
expect($('span:not([class])')).to.have.lengthOf(1, 'span:not([class])');
|
||||||
|
|
||||||
|
expect($('[class="test control"]').text()).to.equal('test control');
|
||||||
|
expect($('[class="test expression"]').text()).to.equal('test expression');
|
||||||
|
expect($('[class="test true"]').text()).to.equal('test true');
|
||||||
|
expect($('[class="test truthy"]').text()).to.equal('test truthy');
|
||||||
|
expect($('[class="test set"]').text()).to.equal('test set');
|
||||||
|
expect($('[class="hello goodbye hello world hello friend"]').text()).to.equal('hello goodbye hello world hello friend');
|
||||||
|
expect($('[class="foo baz"]').text()).to.equal('foo baz');
|
||||||
|
expect($('span:not([class])').text()).to.equal('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<span {...Astro.props} />
|
<span {...Astro.props}>{Astro.props.class}</span>
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
---
|
---
|
||||||
import Component from '../components/Span.astro'
|
import Component from '../components/Span.astro'
|
||||||
---
|
---
|
||||||
<Component class="test control" />
|
|
||||||
|
|
||||||
<!-- @note: `class:list` will not be parsed if its value is not an expression -->
|
<Component class="test control" />
|
||||||
<!-- <Component class:list="test" /> -->
|
|
||||||
|
|
||||||
<Component class:list={'test expression'} />
|
<Component class:list={'test expression'} />
|
||||||
|
|
||||||
|
@ -13,9 +11,9 @@ import Component from '../components/Span.astro'
|
||||||
<Component class:list={{ test: true, true: true, false: false }} />
|
<Component class:list={{ test: true, true: true, false: false }} />
|
||||||
<Component class:list={{ test: 1, truthy: '0', noshow1: 0, noshow2: '', noshow3: null, noshow4: undefined }} />
|
<Component class:list={{ test: 1, truthy: '0', noshow1: 0, noshow2: '', noshow3: null, noshow4: undefined }} />
|
||||||
|
|
||||||
<Component class:list={new Set(['test', 'set'])} />
|
<Component class:list={['test', 'set']} />
|
||||||
|
|
||||||
<Component class:list={[ 'hello goodbye', { hello: true, world: true }, new Set([ 'hello', 'friend' ]) ]} />
|
<Component class:list={[ 'hello goodbye', { hello: true, world: true }, [ 'hello', 'friend' ] ]} />
|
||||||
|
|
||||||
<Component class:list={['foo', false && 'bar', true && 'baz']} />
|
<Component class:list={['foo', false && 'bar', true && 'baz']} />
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
<span class:list={{ test: true, true: true, false: false }} />
|
<span class:list={{ test: true, true: true, false: false }} />
|
||||||
<span class:list={{ test: 1, truthy: '0', noshow1: 0, noshow2: '', noshow3: null, noshow4: undefined }} />
|
<span class:list={{ test: 1, truthy: '0', noshow1: 0, noshow2: '', noshow3: null, noshow4: undefined }} />
|
||||||
|
|
||||||
<span class:list={new Set(['test', 'set'])} />
|
<span class:list={['test', 'set']} />
|
||||||
|
|
||||||
<span class:list={[ 'hello goodbye', { hello: true, world: true }, new Set([ 'hello', 'friend' ]) ]} />
|
<span class:list={[ 'hello goodbye', { hello: true, world: true }, [ 'hello', 'friend' ] ]} />
|
||||||
|
|
||||||
<span class:list={['foo', false && 'bar', true && 'baz']} />
|
<span class:list={['foo', false && 'bar', true && 'baz']} />
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as cheerio from 'cheerio';
|
import * as cheerio from 'cheerio';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import svelte from '../../../../integrations/svelte/dist/index.js';
|
|
||||||
import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js';
|
import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js';
|
||||||
|
|
||||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||||
|
@ -33,7 +32,7 @@ describe('core/render components', () => {
|
||||||
inlineConfig: {
|
inlineConfig: {
|
||||||
root: fileURLToPath(root),
|
root: fileURLToPath(root),
|
||||||
logLevel: 'silent',
|
logLevel: 'silent',
|
||||||
integrations: [svelte()],
|
integrations: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (container) => {
|
async (container) => {
|
||||||
|
@ -56,4 +55,67 @@ describe('core/render components', () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should merge `class` and `class:list`', async () => {
|
||||||
|
const fs = createFs(
|
||||||
|
{
|
||||||
|
'/src/pages/index.astro': `
|
||||||
|
---
|
||||||
|
import Class from '../components/Class.astro';
|
||||||
|
import ClassList from '../components/ClassList.astro';
|
||||||
|
import BothLiteral from '../components/BothLiteral.astro';
|
||||||
|
import BothFlipped from '../components/BothFlipped.astro';
|
||||||
|
import BothSpread from '../components/BothSpread.astro';
|
||||||
|
---
|
||||||
|
<Class class="red blue" />
|
||||||
|
<ClassList class:list={{ red: true, blue: true }} />
|
||||||
|
<BothLiteral class="red" class:list={{ blue: true }} />
|
||||||
|
<BothFlipped class:list={{ blue: true }} class="red" />
|
||||||
|
<BothSpread class:list={{ blue: true }} { ...{ class: "red" }} />
|
||||||
|
`,
|
||||||
|
'/src/components/Class.astro': `<pre id="class" set:html={JSON.stringify(Astro.props)} />`,
|
||||||
|
'/src/components/ClassList.astro': `<pre id="class-list" set:html={JSON.stringify(Astro.props)} />`,
|
||||||
|
'/src/components/BothLiteral.astro': `<pre id="both-literal" set:html={JSON.stringify(Astro.props)} />`,
|
||||||
|
'/src/components/BothFlipped.astro': `<pre id="both-flipped" set:html={JSON.stringify(Astro.props)} />`,
|
||||||
|
'/src/components/BothSpread.astro': `<pre id="both-spread" set:html={JSON.stringify(Astro.props)} />`,
|
||||||
|
},
|
||||||
|
root
|
||||||
|
);
|
||||||
|
|
||||||
|
await runInContainer(
|
||||||
|
{
|
||||||
|
fs,
|
||||||
|
inlineConfig: {
|
||||||
|
root: fileURLToPath(root),
|
||||||
|
logLevel: 'silent',
|
||||||
|
integrations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (container) => {
|
||||||
|
const { req, res, done, text } = createRequestAndResponse({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/',
|
||||||
|
});
|
||||||
|
container.handle(req, res);
|
||||||
|
|
||||||
|
await done;
|
||||||
|
const html = await text();
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
const check = (name) => JSON.parse($(name).text() || '{}')
|
||||||
|
|
||||||
|
const Class = check('#class');
|
||||||
|
const ClassList = check('#class-list');
|
||||||
|
const BothLiteral = check('#both-literal');
|
||||||
|
const BothFlipped = check('#both-flipped');
|
||||||
|
const BothSpread = check('#both-spread');
|
||||||
|
|
||||||
|
expect(Class).to.deep.equal({ class: 'red blue' }, '#class');
|
||||||
|
expect(ClassList).to.deep.equal({ class: 'red blue' }, '#class-list');
|
||||||
|
expect(BothLiteral).to.deep.equal({ class: 'red blue' }, '#both-literal');
|
||||||
|
expect(BothFlipped).to.deep.equal({ class: 'red blue' }, '#both-flipped');
|
||||||
|
expect(BothSpread).to.deep.equal({ class: 'red blue' }, '#both-spread');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -527,6 +527,9 @@ importers:
|
||||||
ci-info:
|
ci-info:
|
||||||
specifier: ^3.8.0
|
specifier: ^3.8.0
|
||||||
version: 3.8.0
|
version: 3.8.0
|
||||||
|
clsx:
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.0.0
|
||||||
common-ancestor-path:
|
common-ancestor-path:
|
||||||
specifier: ^1.0.1
|
specifier: ^1.0.1
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
|
@ -10150,6 +10153,11 @@ packages:
|
||||||
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
|
/clsx@2.0.0:
|
||||||
|
resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/code-block-writer@12.0.0:
|
/code-block-writer@12.0.0:
|
||||||
resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==}
|
resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -18158,25 +18166,21 @@ packages:
|
||||||
file:packages/astro/test/fixtures/css-assets/packages/font-awesome:
|
file:packages/astro/test/fixtures/css-assets/packages/font-awesome:
|
||||||
resolution: {directory: packages/astro/test/fixtures/css-assets/packages/font-awesome, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/css-assets/packages/font-awesome, type: directory}
|
||||||
name: '@test/astro-font-awesome-package'
|
name: '@test/astro-font-awesome-package'
|
||||||
version: 0.0.1
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:packages/astro/test/fixtures/multiple-renderers/renderers/one:
|
file:packages/astro/test/fixtures/multiple-renderers/renderers/one:
|
||||||
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/one, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/one, type: directory}
|
||||||
name: '@test/astro-renderer-one'
|
name: '@test/astro-renderer-one'
|
||||||
version: 1.0.0
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:packages/astro/test/fixtures/multiple-renderers/renderers/two:
|
file:packages/astro/test/fixtures/multiple-renderers/renderers/two:
|
||||||
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/two, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/two, type: directory}
|
||||||
name: '@test/astro-renderer-two'
|
name: '@test/astro-renderer-two'
|
||||||
version: 1.0.0
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:packages/astro/test/fixtures/solid-component/deps/solid-jsx-component:
|
file:packages/astro/test/fixtures/solid-component/deps/solid-jsx-component:
|
||||||
resolution: {directory: packages/astro/test/fixtures/solid-component/deps/solid-jsx-component, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/solid-component/deps/solid-jsx-component, type: directory}
|
||||||
name: '@test/solid-jsx-component'
|
name: '@test/solid-jsx-component'
|
||||||
version: 0.0.0
|
|
||||||
dependencies:
|
dependencies:
|
||||||
solid-js: 1.7.6
|
solid-js: 1.7.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
Loading…
Reference in a new issue