Implement custom client directives (#7074)
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
3a7f6ae918
commit
73ec6f6c16
43 changed files with 765 additions and 86 deletions
51
.changeset/tall-eyes-vanish.md
Normal file
51
.changeset/tall-eyes-vanish.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Integrations can add new `client:` directives through the `astro:config:setup` hook's `addClientDirective()` API. To enable this API, the user needs to set `experimental.customClientDirectives` to `true` in their config.
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'astro/config';
|
||||
import onClickDirective from 'astro-click-directive';
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [onClickDirective()],
|
||||
experimental: {
|
||||
customClientDirectives: true
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
export default function onClickDirective() {
|
||||
return {
|
||||
hooks: {
|
||||
'astro:config:setup': ({ addClientDirective }) => {
|
||||
addClientDirective({
|
||||
name: 'click',
|
||||
entrypoint: 'astro-click-directive/click.js'
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```astro
|
||||
<Counter client:click />
|
||||
```
|
||||
|
||||
The client directive file (e.g. `astro-click-directive/click.js`) should export a function of type `ClientDirective`:
|
||||
|
||||
```ts
|
||||
import type { ClientDirective } from 'astro'
|
||||
|
||||
const clickDirective: ClientDirective = (load, opts, el) => {
|
||||
window.addEventListener('click', async () => {
|
||||
const hydrate = await load()
|
||||
await hydrate()
|
||||
}, { once: true })
|
||||
}
|
||||
|
||||
export default clickDirective
|
||||
```
|
3
packages/astro/astro-jsx.d.ts
vendored
3
packages/astro/astro-jsx.d.ts
vendored
|
@ -18,12 +18,13 @@ declare namespace astroHTML.JSX {
|
|||
children: {};
|
||||
}
|
||||
|
||||
interface IntrinsicAttributes extends AstroBuiltinProps, AstroBuiltinAttributes {
|
||||
interface IntrinsicAttributes extends AstroBuiltinProps, AstroBuiltinAttributes, AstroClientDirectives {
|
||||
slot?: string;
|
||||
children?: Children;
|
||||
}
|
||||
|
||||
type AstroBuiltinProps = import('./dist/@types/astro.js').AstroBuiltinProps;
|
||||
type AstroClientDirectives = import('./dist/@types/astro.js').AstroClientDirectives;
|
||||
type AstroBuiltinAttributes = import('./dist/@types/astro.js').AstroBuiltinAttributes;
|
||||
type AstroDefineVarsAttribute = import('./dist/@types/astro.js').AstroDefineVarsAttribute;
|
||||
type AstroScriptAttributes = import('./dist/@types/astro.js').AstroScriptAttributes &
|
||||
|
|
92
packages/astro/e2e/custom-client-directives.test.js
Normal file
92
packages/astro/e2e/custom-client-directives.test.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
import { expect } from '@playwright/test';
|
||||
import { testFactory, waitForHydrate } from './test-utils.js';
|
||||
import testAdapter from '../test/test-adapter.js';
|
||||
|
||||
const test = testFactory({
|
||||
root: './fixtures/custom-client-directives/',
|
||||
});
|
||||
|
||||
test.describe('Custom Client Directives - dev', () => {
|
||||
let devServer;
|
||||
|
||||
test.beforeAll(async ({ astro }) => {
|
||||
devServer = await astro.startDevServer();
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await devServer.stop();
|
||||
});
|
||||
|
||||
testClientDirectivesShared();
|
||||
});
|
||||
|
||||
test.describe('Custom Client Directives - build static', () => {
|
||||
let previewServer;
|
||||
|
||||
test.beforeAll(async ({ astro }) => {
|
||||
await astro.build();
|
||||
previewServer = await astro.preview();
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await previewServer.stop();
|
||||
});
|
||||
|
||||
testClientDirectivesShared();
|
||||
});
|
||||
|
||||
test.describe('Custom Client Directives - build server', () => {
|
||||
let previewServer;
|
||||
|
||||
test.beforeAll(async ({ astro }) => {
|
||||
await astro.build({
|
||||
adapter: testAdapter(),
|
||||
});
|
||||
previewServer = await astro.preview();
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await previewServer.stop();
|
||||
});
|
||||
|
||||
testClientDirectivesShared();
|
||||
});
|
||||
|
||||
function testClientDirectivesShared() {
|
||||
test('client:click should work', async ({ astro, page }) => {
|
||||
await page.goto(astro.resolveUrl('/'));
|
||||
|
||||
const incrementBtn = page.locator('#client-click .increment');
|
||||
const counterValue = page.locator('#client-click pre');
|
||||
|
||||
await expect(counterValue).toHaveText('0');
|
||||
|
||||
// Component only hydrates on first click
|
||||
await Promise.all([waitForHydrate(page, counterValue), incrementBtn.click()]);
|
||||
|
||||
// Since first click only triggers hydration, this should stay 0
|
||||
await expect(counterValue).toHaveText('0');
|
||||
await incrementBtn.click();
|
||||
// Hydrated, this should be 1
|
||||
await expect(counterValue).toHaveText('1');
|
||||
});
|
||||
|
||||
test('client:password should work', async ({ astro, page }) => {
|
||||
await page.goto(astro.resolveUrl('/'));
|
||||
|
||||
const incrementBtn = page.locator('#client-password .increment');
|
||||
const counterValue = page.locator('#client-password pre');
|
||||
|
||||
await expect(counterValue).toHaveText('0');
|
||||
await incrementBtn.click();
|
||||
// Not hydrated, so this should stay 0
|
||||
await expect(counterValue).toHaveText('0');
|
||||
|
||||
// Type super cool password to activate password!
|
||||
await Promise.all([waitForHydrate(page, counterValue), page.keyboard.type('hunter2')]);
|
||||
|
||||
await incrementBtn.click();
|
||||
// Hydrated, this should be 1
|
||||
await expect(counterValue).toHaveText('1');
|
||||
});
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import react from "@astrojs/react";
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [astroClientClickDirective(), astroClientPasswordDirective(), react()],
|
||||
experimental: {
|
||||
customClientDirectives: true
|
||||
}
|
||||
});
|
||||
|
||||
function astroClientClickDirective() {
|
||||
return {
|
||||
name: 'astro-client-click',
|
||||
hooks: {
|
||||
'astro:config:setup': (opts) => {
|
||||
opts.addClientDirective({
|
||||
name: 'click',
|
||||
entrypoint: fileURLToPath(new URL('./client-click.js', import.meta.url))
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function astroClientPasswordDirective() {
|
||||
return {
|
||||
name: 'astro-client-click',
|
||||
hooks: {
|
||||
'astro:config:setup': (opts) => {
|
||||
opts.addClientDirective({
|
||||
name: 'password',
|
||||
entrypoint: fileURLToPath(new URL('./client-password.js', import.meta.url))
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// Hydrate on first click on the window
|
||||
export default (load) => {
|
||||
window.addEventListener('click', async () => {
|
||||
const hydrate = await load()
|
||||
await hydrate()
|
||||
}, { once: true })
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Hydrate when the user types the correct password
|
||||
export default (load, options) => {
|
||||
const password = options.value
|
||||
let consecutiveMatch = 0
|
||||
|
||||
const handleKeydown = async (e) => {
|
||||
if (e.key === password[consecutiveMatch]) {
|
||||
consecutiveMatch++
|
||||
} else {
|
||||
consecutiveMatch = 0
|
||||
}
|
||||
|
||||
if (consecutiveMatch === password.length) {
|
||||
window.removeEventListener('keydown', handleKeydown)
|
||||
const hydrate = await load()
|
||||
await hydrate()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleKeydown)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "@test/custom-client-directives",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/react": "workspace:*",
|
||||
"astro": "workspace:*",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
}
|
9
packages/astro/e2e/fixtures/custom-client-directives/src/client-directives-types.d.ts
vendored
Normal file
9
packages/astro/e2e/fixtures/custom-client-directives/src/client-directives-types.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
declare module 'astro' {
|
||||
interface AstroClientDirectives {
|
||||
'client:click'?: boolean
|
||||
'client:password'?: string
|
||||
}
|
||||
}
|
||||
|
||||
// Make d.ts a module to similate common packaging setups where the entry `index.d.ts` would augment the types
|
||||
export {}
|
|
@ -0,0 +1,18 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
export default function Counter({ children, count: initialCount = 0, id }) {
|
||||
const [count, setCount] = useState(initialCount);
|
||||
const add = () => setCount((i) => i + 1);
|
||||
const subtract = () => setCount((i) => i - 1);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id={id} className="counter">
|
||||
<button className="decrement" onClick={subtract}>-</button>
|
||||
<pre>{count}</pre>
|
||||
<button className="increment" onClick={add}>+</button>
|
||||
</div>
|
||||
<div className="counter-message">{children}</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
import Counter from '../components/Counter.jsx';
|
||||
---
|
||||
|
||||
<html>
|
||||
<body>
|
||||
<Counter id="client-click" client:click>client:click</Counter>
|
||||
<Counter id="client-password" client:password="hunter2">client:password</Counter>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// preserveSymlinks set to true so the augmented `declare module 'astro'` works.
|
||||
// This is only needed because we link Astro locally.
|
||||
"preserveSymlinks": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
|
@ -55,7 +55,6 @@ export async function getErrorOverlayContent(page) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Locator} el
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
export async function getColor(el) {
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
"devalue": "^4.2.0",
|
||||
"diff": "^5.1.0",
|
||||
"es-module-lexer": "^1.1.0",
|
||||
"esbuild": "^0.17.18",
|
||||
"estree-walker": "3.0.0",
|
||||
"execa": "^6.1.0",
|
||||
"fast-glob": "^3.2.11",
|
||||
|
|
|
@ -55,6 +55,10 @@ export interface AstroBuiltinProps {
|
|||
'client:only'?: boolean | string;
|
||||
}
|
||||
|
||||
// Allow users to extend this for astro-jsx.d.ts
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface AstroClientDirectives {}
|
||||
|
||||
export interface AstroBuiltinAttributes {
|
||||
'class:list'?:
|
||||
| Record<string, boolean>
|
||||
|
@ -1075,6 +1079,28 @@ export interface AstroUserConfig {
|
|||
*/
|
||||
inlineStylesheets?: 'always' | 'auto' | 'never';
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name experimental.customClientDirectives
|
||||
* @type {boolean}
|
||||
* @default `false`
|
||||
* @version 2.5.0
|
||||
* @description
|
||||
* Allow integrations to use the [experimental `addClientDirective` API](/en/reference/integrations-reference/#addclientdirective-option) in the `astro:config:setup` hook
|
||||
* to add custom client directives in Astro files.
|
||||
*
|
||||
* To enable this feature, set `experimental.customClientDirectives` to `true` in your Astro config:
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* experimental: {
|
||||
* customClientDirectives: true,
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
customClientDirectives?: boolean;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name experimental.middleware
|
||||
|
@ -1206,6 +1232,10 @@ export interface AstroSettings {
|
|||
stage: InjectedScriptStage;
|
||||
content: string;
|
||||
}[];
|
||||
/**
|
||||
* Map of directive name (e.g. `load`) to the directive script code
|
||||
*/
|
||||
clientDirectives: Map<string, string>;
|
||||
tsConfig: TsConfigJson | undefined;
|
||||
tsConfigPath: string | undefined;
|
||||
watchFiles: string[];
|
||||
|
@ -1654,6 +1684,7 @@ export interface AstroIntegration {
|
|||
addWatchFile: (path: URL | string) => void;
|
||||
injectScript: (stage: InjectedScriptStage, content: string) => void;
|
||||
injectRoute: (injectRoute: InjectedRoute) => void;
|
||||
addClientDirective: (directive: ClientDirectiveConfig) => void;
|
||||
// TODO: Add support for `injectElement()` for full HTML element injection, not just scripts.
|
||||
// This may require some refactoring of `scripts`, `styles`, and `links` into something
|
||||
// more generalized. Consider the SSR use-case as well.
|
||||
|
@ -1750,6 +1781,7 @@ export interface SSRMetadata {
|
|||
hasDirectives: Set<string>;
|
||||
hasRenderedHead: boolean;
|
||||
headInTree: boolean;
|
||||
clientDirectives: Map<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1815,3 +1847,29 @@ export type CreatePreviewServer = (
|
|||
export interface PreviewModule {
|
||||
default: CreatePreviewServer;
|
||||
}
|
||||
|
||||
/* Client Directives */
|
||||
type DirectiveHydrate = () => Promise<void>;
|
||||
type DirectiveLoad = () => Promise<DirectiveHydrate>;
|
||||
|
||||
type DirectiveOptions = {
|
||||
/**
|
||||
* The component displayName
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The attribute value provided
|
||||
*/
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type ClientDirective = (
|
||||
load: DirectiveLoad,
|
||||
options: DirectiveOptions,
|
||||
el: HTMLElement
|
||||
) => void;
|
||||
|
||||
export interface ClientDirectiveConfig {
|
||||
name: string;
|
||||
entrypoint: string;
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@ export function deserializeManifest(serializedManifest: SerializedSSRManifest):
|
|||
|
||||
const assets = new Set<string>(serializedManifest.assets);
|
||||
const componentMetadata = new Map(serializedManifest.componentMetadata);
|
||||
const clientDirectives = new Map(serializedManifest.clientDirectives);
|
||||
|
||||
return {
|
||||
...serializedManifest,
|
||||
assets,
|
||||
componentMetadata,
|
||||
clientDirectives,
|
||||
routes,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ export class App {
|
|||
markdown: manifest.markdown,
|
||||
mode: 'production',
|
||||
renderers: manifest.renderers,
|
||||
clientDirectives: manifest.clientDirectives,
|
||||
async resolve(specifier: string) {
|
||||
if (!(specifier in manifest.entryModules)) {
|
||||
throw new Error(`Unable to resolve [${specifier}]`);
|
||||
|
|
|
@ -41,16 +41,24 @@ export interface SSRManifest {
|
|||
markdown: MarkdownRenderingOptions;
|
||||
pageMap: Map<ComponentPath, ComponentInstance>;
|
||||
renderers: SSRLoadedRenderer[];
|
||||
/**
|
||||
* Map of directive name (e.g. `load`) to the directive script code
|
||||
*/
|
||||
clientDirectives: Map<string, string>;
|
||||
entryModules: Record<string, string>;
|
||||
assets: Set<string>;
|
||||
componentMetadata: SSRResult['componentMetadata'];
|
||||
middleware?: AstroMiddlewareInstance<unknown>;
|
||||
}
|
||||
|
||||
export type SerializedSSRManifest = Omit<SSRManifest, 'routes' | 'assets' | 'componentMetadata'> & {
|
||||
export type SerializedSSRManifest = Omit<
|
||||
SSRManifest,
|
||||
'routes' | 'assets' | 'componentMetadata' | 'clientDirectives'
|
||||
> & {
|
||||
routes: SerializedRouteInfo[];
|
||||
assets: string[];
|
||||
componentMetadata: [string, SSRComponentMetadata][];
|
||||
clientDirectives: [string, string][];
|
||||
};
|
||||
|
||||
export type AdapterCreateExports<T = any> = (
|
||||
|
|
|
@ -417,6 +417,7 @@ async function generatePath(
|
|||
markdown: settings.config.markdown,
|
||||
mode: opts.mode,
|
||||
renderers,
|
||||
clientDirectives: settings.clientDirectives,
|
||||
async resolve(specifier: string) {
|
||||
const hashedFilePath = internals.entrySpecifierToBundleMap.get(specifier);
|
||||
if (typeof hashedFilePath !== 'string') {
|
||||
|
|
|
@ -237,6 +237,7 @@ function buildManifest(
|
|||
pageMap: null as any,
|
||||
componentMetadata: Array.from(internals.componentMetadata),
|
||||
renderers: [],
|
||||
clientDirectives: Array.from(settings.clientDirectives),
|
||||
entryModules,
|
||||
assets: staticFiles.map(prefixAssetPath),
|
||||
};
|
||||
|
|
33
packages/astro/src/core/client-directive/build.ts
Normal file
33
packages/astro/src/core/client-directive/build.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { build } from 'esbuild';
|
||||
|
||||
/**
|
||||
* Build a client directive entrypoint into code that can directly run in a `<script>` tag.
|
||||
*/
|
||||
export async function buildClientDirectiveEntrypoint(name: string, entrypoint: string) {
|
||||
const stringifiedName = JSON.stringify(name);
|
||||
const stringifiedEntrypoint = JSON.stringify(entrypoint);
|
||||
|
||||
// NOTE: when updating this stdin code, make sure to also update `packages/astro/scripts/prebuild.ts`
|
||||
// that prebuilds the client directive with a similar code too.
|
||||
const output = await build({
|
||||
stdin: {
|
||||
contents: `\
|
||||
import directive from ${stringifiedEntrypoint};
|
||||
|
||||
(self.Astro || (self.Astro = {}))[${stringifiedName}] = directive;
|
||||
|
||||
window.dispatchEvent(new Event('astro:' + ${stringifiedName}));`,
|
||||
resolveDir: process.cwd(),
|
||||
},
|
||||
absWorkingDir: process.cwd(),
|
||||
format: 'iife',
|
||||
minify: true,
|
||||
bundle: true,
|
||||
write: false,
|
||||
});
|
||||
|
||||
const outputFile = output.outputFiles?.[0];
|
||||
if (!outputFile) return '';
|
||||
|
||||
return outputFile.text;
|
||||
}
|
15
packages/astro/src/core/client-directive/default.ts
Normal file
15
packages/astro/src/core/client-directive/default.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import idlePrebuilt from '../../runtime/client/idle.prebuilt.js';
|
||||
import loadPrebuilt from '../../runtime/client/load.prebuilt.js';
|
||||
import mediaPrebuilt from '../../runtime/client/media.prebuilt.js';
|
||||
import onlyPrebuilt from '../../runtime/client/only.prebuilt.js';
|
||||
import visiblePrebuilt from '../../runtime/client/visible.prebuilt.js';
|
||||
|
||||
export function getDefaultClientDirectives() {
|
||||
return new Map([
|
||||
['idle', idlePrebuilt],
|
||||
['load', loadPrebuilt],
|
||||
['media', mediaPrebuilt],
|
||||
['only', onlyPrebuilt],
|
||||
['visible', visiblePrebuilt],
|
||||
]);
|
||||
}
|
2
packages/astro/src/core/client-directive/index.ts
Normal file
2
packages/astro/src/core/client-directive/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { buildClientDirectiveEntrypoint } from './build.js';
|
||||
export { getDefaultClientDirectives } from './default.js';
|
|
@ -38,6 +38,7 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
|
|||
legacy: {},
|
||||
experimental: {
|
||||
assets: false,
|
||||
customClientDirecives: false,
|
||||
inlineStylesheets: 'never',
|
||||
middleware: false,
|
||||
},
|
||||
|
@ -195,6 +196,10 @@ export const AstroConfigSchema = z.object({
|
|||
experimental: z
|
||||
.object({
|
||||
assets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.assets),
|
||||
customClientDirectives: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.experimental.customClientDirecives),
|
||||
inlineStylesheets: z
|
||||
.enum(['always', 'auto', 'never'])
|
||||
.optional()
|
||||
|
|
|
@ -7,6 +7,7 @@ import { markdownContentEntryType } from '../../vite-plugin-markdown/content-ent
|
|||
import { createDefaultDevConfig } from './config.js';
|
||||
import { AstroTimer } from './timer.js';
|
||||
import { loadTSConfig } from './tsconfig.js';
|
||||
import { getDefaultClientDirectives } from '../client-directive/index.js';
|
||||
|
||||
export function createBaseSettings(config: AstroConfig): AstroSettings {
|
||||
return {
|
||||
|
@ -23,6 +24,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings {
|
|||
contentEntryTypes: [markdownContentEntryType],
|
||||
renderers: [jsxRenderer],
|
||||
scripts: [],
|
||||
clientDirectives: getDefaultClientDirectives(),
|
||||
watchFiles: [],
|
||||
forceDisableTelemetry: false,
|
||||
timer: new AstroTimer(),
|
||||
|
|
|
@ -140,6 +140,7 @@ export async function renderPage({ mod, renderContext, env, apiContext }: Render
|
|||
componentMetadata: renderContext.componentMetadata,
|
||||
resolve: env.resolve,
|
||||
renderers: env.renderers,
|
||||
clientDirectives: env.clientDirectives,
|
||||
request: renderContext.request,
|
||||
site: env.site,
|
||||
scripts: renderContext.scripts,
|
||||
|
|
|
@ -25,6 +25,7 @@ export function createDevelopmentEnvironment(
|
|||
mode,
|
||||
// This will be overridden in the dev server
|
||||
renderers: [],
|
||||
clientDirectives: settings.clientDirectives,
|
||||
resolve: createResolve(loader, settings.config.root),
|
||||
routeCache: new RouteCache(logging, mode),
|
||||
site: settings.config.site,
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark';
|
|||
import type { RuntimeMode, SSRLoadedRenderer } from '../../@types/astro';
|
||||
import type { LogOptions } from '../logger/core.js';
|
||||
import { RouteCache } from './route-cache.js';
|
||||
import { getDefaultClientDirectives } from '../client-directive/default.js';
|
||||
|
||||
/**
|
||||
* An environment represents the static parts of rendering that do not change
|
||||
|
@ -16,6 +17,7 @@ export interface Environment {
|
|||
/** "development" or "production" */
|
||||
mode: RuntimeMode;
|
||||
renderers: SSRLoadedRenderer[];
|
||||
clientDirectives: Map<string, string>;
|
||||
resolve: (s: string) => Promise<string>;
|
||||
routeCache: RouteCache;
|
||||
site?: string;
|
||||
|
@ -46,6 +48,7 @@ export function createBasicEnvironment(options: CreateBasicEnvironmentArgs): Env
|
|||
},
|
||||
mode,
|
||||
renderers: options.renderers ?? [],
|
||||
clientDirectives: getDefaultClientDirectives(),
|
||||
resolve: options.resolve ?? ((s: string) => Promise.resolve(s)),
|
||||
routeCache: new RouteCache(options.logging, mode),
|
||||
ssr: options.ssr ?? true,
|
||||
|
|
|
@ -42,6 +42,7 @@ export interface CreateResultArgs {
|
|||
pathname: string;
|
||||
props: Props;
|
||||
renderers: SSRLoadedRenderer[];
|
||||
clientDirectives: Map<string, string>;
|
||||
resolve: (s: string) => Promise<string>;
|
||||
site: string | undefined;
|
||||
links?: Set<SSRElement>;
|
||||
|
@ -132,7 +133,8 @@ class Slots {
|
|||
let renderMarkdown: any = null;
|
||||
|
||||
export function createResult(args: CreateResultArgs): SSRResult {
|
||||
const { markdown, params, pathname, renderers, request, resolve, locals } = args;
|
||||
const { markdown, params, pathname, renderers, clientDirectives, request, resolve, locals } =
|
||||
args;
|
||||
|
||||
const url = new URL(request.url);
|
||||
const headers = new Headers();
|
||||
|
@ -260,6 +262,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
|
|||
hasRenderedHead: false,
|
||||
hasDirectives: new Set(),
|
||||
headInTree: false,
|
||||
clientDirectives,
|
||||
},
|
||||
response,
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ import type { PageBuildData } from '../core/build/types';
|
|||
import { mergeConfig } from '../core/config/config.js';
|
||||
import { info, type LogOptions } from '../core/logger/core.js';
|
||||
import { mdxContentEntryType } from '../vite-plugin-markdown/content-entry-type.js';
|
||||
import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js';
|
||||
|
||||
async function withTakingALongTimeMsg<T>({
|
||||
name,
|
||||
|
@ -55,6 +56,7 @@ export async function runHookConfigSetup({
|
|||
|
||||
let updatedConfig: AstroConfig = { ...settings.config };
|
||||
let updatedSettings: AstroSettings = { ...settings, config: updatedConfig };
|
||||
let addedClientDirectives = new Map<string, Promise<string>>();
|
||||
|
||||
for (const integration of settings.config.integrations) {
|
||||
/**
|
||||
|
@ -97,6 +99,19 @@ export async function runHookConfigSetup({
|
|||
addWatchFile: (path) => {
|
||||
updatedSettings.watchFiles.push(path instanceof URL ? fileURLToPath(path) : path);
|
||||
},
|
||||
addClientDirective: ({ name, entrypoint }) => {
|
||||
if (!settings.config.experimental.customClientDirectives) {
|
||||
throw new Error(
|
||||
`The "${integration.name}" integration is trying to add the "${name}" client directive, but the \`experimental.customClientDirectives\` config is not enabled.`
|
||||
);
|
||||
}
|
||||
if (updatedSettings.clientDirectives.has(name) || addedClientDirectives.has(name)) {
|
||||
throw new Error(
|
||||
`The "${integration.name}" integration is trying to add the "${name}" client directive, but it already exists.`
|
||||
);
|
||||
}
|
||||
addedClientDirectives.set(name, buildClientDirectiveEntrypoint(name, entrypoint));
|
||||
},
|
||||
};
|
||||
|
||||
// ---
|
||||
|
@ -138,6 +153,11 @@ export async function runHookConfigSetup({
|
|||
) {
|
||||
addContentEntryType(mdxContentEntryType);
|
||||
}
|
||||
|
||||
// Add custom client directives to settings, waiting for compiled code by esbuild
|
||||
for (const [name, compiled] of addedClientDirectives) {
|
||||
updatedSettings.clientDirectives.set(name, await compiled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import * as t from '@babel/types';
|
|||
import { AstroErrorData } from '../core/errors/errors-data.js';
|
||||
import { AstroError } from '../core/errors/errors.js';
|
||||
import { resolvePath } from '../core/util.js';
|
||||
import { HydrationDirectiveProps } from '../runtime/server/hydration.js';
|
||||
import type { PluginMetadata } from '../vite-plugin-astro/types';
|
||||
|
||||
const ClientOnlyPlaceholder = 'astro-client-only';
|
||||
|
@ -285,7 +284,7 @@ export default function astroJSX(): PluginObj {
|
|||
for (const attr of parentNode.openingElement.attributes) {
|
||||
if (t.isJSXAttribute(attr)) {
|
||||
const name = jsxAttributeToString(attr);
|
||||
if (HydrationDirectiveProps.has(name)) {
|
||||
if (name.startsWith('client:')) {
|
||||
// eslint-disable-next-line
|
||||
console.warn(
|
||||
`You are attempting to render <${displayName} ${name} />, but ${displayName} is an Astro component. Astro components do not render in the client and should not have a hydration directive. Please use a framework component for client rendering.`
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
(self.Astro = self.Astro || {}).idle = (getHydrateCallback) => {
|
||||
import type { ClientDirective } from '../../@types/astro';
|
||||
|
||||
const idleDirective: ClientDirective = (load) => {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
const hydrate = await load();
|
||||
await hydrate();
|
||||
};
|
||||
|
||||
if ('requestIdleCallback' in window) {
|
||||
(window as any).requestIdleCallback(cb);
|
||||
} else {
|
||||
setTimeout(cb, 200);
|
||||
}
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:idle'));
|
||||
|
||||
export default idleDirective;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
(self.Astro = self.Astro || {}).load = (getHydrateCallback) => {
|
||||
(async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
})();
|
||||
import type { ClientDirective } from '../../@types/astro';
|
||||
|
||||
const loadDirective: ClientDirective = async (load) => {
|
||||
const hydrate = await load();
|
||||
await hydrate();
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:load'));
|
||||
|
||||
export default loadDirective;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import type { ClientDirective } from '../../@types/astro';
|
||||
|
||||
/**
|
||||
* Hydrate this component when a matching media query is found
|
||||
*/
|
||||
(self.Astro = self.Astro || {}).media = (getHydrateCallback, options) => {
|
||||
const mediaDirective: ClientDirective = (load, options) => {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
const hydrate = await load();
|
||||
await hydrate();
|
||||
};
|
||||
|
||||
|
@ -16,4 +18,5 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:media'));
|
||||
|
||||
export default mediaDirective;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import type { ClientDirective } from '../../@types/astro';
|
||||
|
||||
/**
|
||||
* Hydrate this component only on the client
|
||||
*/
|
||||
(self.Astro = self.Astro || {}).only = (getHydrateCallback) => {
|
||||
(async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
})();
|
||||
const onlyDirective: ClientDirective = async (load) => {
|
||||
const hydrate = await load();
|
||||
await hydrate();
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:only'));
|
||||
|
||||
export default onlyDirective;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import type { ClientDirective } from '../../@types/astro';
|
||||
|
||||
/**
|
||||
* Hydrate this component when one of it's children becomes visible
|
||||
* We target the children because `astro-island` is set to `display: contents`
|
||||
* which doesn't work with IntersectionObserver
|
||||
*/
|
||||
(self.Astro = self.Astro || {}).visible = (getHydrateCallback, _opts, root) => {
|
||||
const visibleDirective: ClientDirective = (load, _options, el) => {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
const hydrate = await load();
|
||||
await hydrate();
|
||||
};
|
||||
|
||||
let io = new IntersectionObserver((entries) => {
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (!entry.isIntersecting) continue;
|
||||
// As soon as we hydrate, disconnect this IntersectionObserver for every `astro-island`
|
||||
|
@ -19,9 +21,10 @@
|
|||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < root.children.length; i++) {
|
||||
const child = root.children[i];
|
||||
for (let i = 0; i < el.children.length; i++) {
|
||||
const child = el.children[i];
|
||||
io.observe(child);
|
||||
}
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:visible'));
|
||||
|
||||
export default visibleDirective;
|
||||
|
|
|
@ -9,10 +9,6 @@ import { escapeHTML } from './escape.js';
|
|||
import { serializeProps } from './serialize.js';
|
||||
import { serializeListValue } from './util.js';
|
||||
|
||||
const HydrationDirectivesRaw = ['load', 'idle', 'media', 'visible', 'only'];
|
||||
const HydrationDirectives = new Set(HydrationDirectivesRaw);
|
||||
export const HydrationDirectiveProps = new Set(HydrationDirectivesRaw.map((n) => `client:${n}`));
|
||||
|
||||
export interface HydrationMetadata {
|
||||
directive: string;
|
||||
value: string;
|
||||
|
@ -29,8 +25,8 @@ interface ExtractedProps {
|
|||
// Used to extract the directives, aka `client:load` information about a component.
|
||||
// Finds these special props and removes them from what gets passed into the component.
|
||||
export function extractDirectives(
|
||||
displayName: string,
|
||||
inputProps: Record<string | number | symbol, any>
|
||||
inputProps: Record<string | number | symbol, any>,
|
||||
clientDirectives: SSRResult['_metadata']['clientDirectives']
|
||||
): ExtractedProps {
|
||||
let extracted: ExtractedProps = {
|
||||
isPage: false,
|
||||
|
@ -74,11 +70,12 @@ export function extractDirectives(
|
|||
extracted.hydration.value = value;
|
||||
|
||||
// throw an error if an invalid hydration directive was provided
|
||||
if (!HydrationDirectives.has(extracted.hydration.directive)) {
|
||||
if (!clientDirectives.has(extracted.hydration.directive)) {
|
||||
const hydrationMethods = Array.from(clientDirectives.keys())
|
||||
.map((d) => `client:${d}`)
|
||||
.join(', ');
|
||||
throw new Error(
|
||||
`Error: invalid hydration directive "${key}". Supported hydration methods: ${Array.from(
|
||||
HydrationDirectiveProps
|
||||
).join(', ')}`
|
||||
`Error: invalid hydration directive "${key}". Supported hydration methods: ${hydrationMethods}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { SSRResult } from '../../../../@types/astro';
|
|||
import type { ComponentSlots } from '../slot.js';
|
||||
import type { AstroComponentFactory, AstroFactoryReturnValue } from './factory.js';
|
||||
|
||||
import { HydrationDirectiveProps } from '../../hydration.js';
|
||||
import { isPromise } from '../../util.js';
|
||||
import { renderChild } from '../any.js';
|
||||
import { isAPropagatingComponent } from './factory.js';
|
||||
|
@ -62,7 +61,7 @@ export class AstroComponentInstance {
|
|||
function validateComponentProps(props: any, displayName: string) {
|
||||
if (props != null) {
|
||||
for (const prop of Object.keys(props)) {
|
||||
if (HydrationDirectiveProps.has(prop)) {
|
||||
if (prop.startsWith('client:')) {
|
||||
// eslint-disable-next-line
|
||||
console.warn(
|
||||
`You are attempting to render <${displayName} ${prop} />, but ${displayName} is an Astro component. Astro components do not render in the client and should not have a hydration directive. Please use a framework component for client rendering.`
|
||||
|
|
|
@ -39,7 +39,7 @@ export function stringifyChunk(
|
|||
? 'directive'
|
||||
: null;
|
||||
if (prescriptType) {
|
||||
let prescripts = getPrescripts(prescriptType, hydration.directive);
|
||||
let prescripts = getPrescripts(result, prescriptType, hydration.directive);
|
||||
return markHTMLString(prescripts);
|
||||
} else {
|
||||
return '';
|
||||
|
|
|
@ -67,10 +67,10 @@ async function renderFrameworkComponent(
|
|||
);
|
||||
}
|
||||
|
||||
const { renderers } = result._metadata;
|
||||
const { renderers, clientDirectives } = result._metadata;
|
||||
const metadata: AstroComponentMetadata = { displayName };
|
||||
|
||||
const { hydration, isPage, props } = extractDirectives(displayName, _props);
|
||||
const { hydration, isPage, props } = extractDirectives(_props, clientDirectives);
|
||||
let html = '';
|
||||
let attrs: Record<string, string> | undefined = undefined;
|
||||
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import type { SSRResult } from '../../@types/astro';
|
||||
|
||||
import idlePrebuilt from '../client/idle.prebuilt.js';
|
||||
import loadPrebuilt from '../client/load.prebuilt.js';
|
||||
import mediaPrebuilt from '../client/media.prebuilt.js';
|
||||
import onlyPrebuilt from '../client/only.prebuilt.js';
|
||||
import visiblePrebuilt from '../client/visible.prebuilt.js';
|
||||
import islandScript from './astro-island.prebuilt.js';
|
||||
|
||||
const ISLAND_STYLES = `<style>astro-island,astro-slot{display:contents}</style>`;
|
||||
|
||||
export function determineIfNeedsHydrationScript(result: SSRResult): boolean {
|
||||
if (result._metadata.hasHydrationScript) {
|
||||
return false;
|
||||
|
@ -14,14 +10,6 @@ export function determineIfNeedsHydrationScript(result: SSRResult): boolean {
|
|||
return (result._metadata.hasHydrationScript = true);
|
||||
}
|
||||
|
||||
export const hydrationScripts: Record<string, string> = {
|
||||
idle: idlePrebuilt,
|
||||
load: loadPrebuilt,
|
||||
only: onlyPrebuilt,
|
||||
media: mediaPrebuilt,
|
||||
visible: visiblePrebuilt,
|
||||
};
|
||||
|
||||
export function determinesIfNeedsDirectiveScript(result: SSRResult, directive: string): boolean {
|
||||
if (result._metadata.hasDirectives.has(directive)) {
|
||||
return false;
|
||||
|
@ -32,26 +20,28 @@ export function determinesIfNeedsDirectiveScript(result: SSRResult, directive: s
|
|||
|
||||
export type PrescriptType = null | 'both' | 'directive';
|
||||
|
||||
function getDirectiveScriptText(directive: string): string {
|
||||
if (!(directive in hydrationScripts)) {
|
||||
function getDirectiveScriptText(result: SSRResult, directive: string): string {
|
||||
const clientDirectives = result._metadata.clientDirectives;
|
||||
const clientDirective = clientDirectives.get(directive);
|
||||
if (!clientDirective) {
|
||||
throw new Error(`Unknown directive: ${directive}`);
|
||||
}
|
||||
const directiveScriptText = hydrationScripts[directive];
|
||||
return directiveScriptText;
|
||||
return clientDirective;
|
||||
}
|
||||
|
||||
export function getPrescripts(type: PrescriptType, directive: string): string {
|
||||
export function getPrescripts(result: SSRResult, type: PrescriptType, directive: string): string {
|
||||
// Note that this is a classic script, not a module script.
|
||||
// This is so that it executes immediate, and when the browser encounters
|
||||
// an astro-island element the callbacks will fire immediately, causing the JS
|
||||
// deps to be loaded immediately.
|
||||
switch (type) {
|
||||
case 'both':
|
||||
return `<style>astro-island,astro-slot{display:contents}</style><script>${
|
||||
getDirectiveScriptText(directive) + islandScript
|
||||
}</script>`;
|
||||
return `${ISLAND_STYLES}<script>${getDirectiveScriptText(
|
||||
result,
|
||||
directive
|
||||
)};${islandScript}</script>`;
|
||||
case 'directive':
|
||||
return `<script>${getDirectiveScriptText(directive)}</script>`;
|
||||
return `<script>${getDirectiveScriptText(result, directive)}</script>`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -127,12 +127,21 @@ export async function loadFixture(inlineConfig) {
|
|||
if (inlineConfig.base && !inlineConfig.base.endsWith('/')) {
|
||||
config.base = inlineConfig.base + '/';
|
||||
}
|
||||
let settings = createSettings(config, fileURLToPath(cwd));
|
||||
if (config.integrations.find((integration) => integration.name === '@astrojs/mdx')) {
|
||||
// Enable default JSX integration. It needs to come first, so unshift rather than push!
|
||||
const { default: jsxRenderer } = await import('astro/jsx/renderer.js');
|
||||
settings.renderers.unshift(jsxRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* The dev/build/sync/check commands run integrations' `astro:config:setup` hook that could mutate
|
||||
* the `AstroSettings`. This function helps to create a fresh settings object that is used by the
|
||||
* command functions below to prevent tests from polluting each other.
|
||||
*/
|
||||
const getSettings = async () => {
|
||||
let settings = createSettings(config, fileURLToPath(cwd));
|
||||
if (config.integrations.find((integration) => integration.name === '@astrojs/mdx')) {
|
||||
// Enable default JSX integration. It needs to come first, so unshift rather than push!
|
||||
const { default: jsxRenderer } = await import('astro/jsx/renderer.js');
|
||||
settings.renderers.unshift(jsxRenderer);
|
||||
}
|
||||
return settings;
|
||||
};
|
||||
|
||||
/** @type {import('@astrojs/telemetry').AstroTelemetry} */
|
||||
const telemetry = {
|
||||
|
@ -170,17 +179,17 @@ export async function loadFixture(inlineConfig) {
|
|||
let devServer;
|
||||
|
||||
return {
|
||||
build: (opts = {}) => {
|
||||
build: async (opts = {}) => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
return build(settings, { logging, telemetry, ...opts });
|
||||
return build(await getSettings(), { logging, telemetry, ...opts });
|
||||
},
|
||||
sync: (opts) => sync(settings, { logging, fs, ...opts }),
|
||||
sync: async (opts) => sync(await getSettings(), { logging, fs, ...opts }),
|
||||
check: async (opts) => {
|
||||
return await check(settings, { logging, ...opts });
|
||||
return await check(await getSettings(), { logging, ...opts });
|
||||
},
|
||||
startDevServer: async (opts = {}) => {
|
||||
process.env.NODE_ENV = 'development';
|
||||
devServer = await dev(settings, { logging, telemetry, ...opts });
|
||||
devServer = await dev(await getSettings(), { logging, telemetry, ...opts });
|
||||
config.server.host = parseAddressToHost(devServer.address.address); // update host
|
||||
config.server.port = devServer.address.port; // update port
|
||||
return devServer;
|
||||
|
@ -202,7 +211,7 @@ export async function loadFixture(inlineConfig) {
|
|||
},
|
||||
preview: async (opts = {}) => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
const previewServer = await preview(settings, {
|
||||
const previewServer = await preview(await getSettings(), {
|
||||
logging,
|
||||
telemetry,
|
||||
...opts,
|
||||
|
|
225
pnpm-lock.yaml
225
pnpm-lock.yaml
|
@ -603,6 +603,9 @@ importers:
|
|||
es-module-lexer:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.1
|
||||
esbuild:
|
||||
specifier: ^0.17.18
|
||||
version: 0.17.18
|
||||
estree-walker:
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
|
@ -941,6 +944,21 @@ importers:
|
|||
specifier: workspace:*
|
||||
version: link:../../..
|
||||
|
||||
packages/astro/e2e/fixtures/custom-client-directives:
|
||||
dependencies:
|
||||
'@astrojs/react':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../integrations/react
|
||||
astro:
|
||||
specifier: workspace:*
|
||||
version: link:../../..
|
||||
react:
|
||||
specifier: ^18.0.0
|
||||
version: 18.2.0
|
||||
react-dom:
|
||||
specifier: ^18.0.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
|
||||
packages/astro/e2e/fixtures/error-cyclic:
|
||||
dependencies:
|
||||
'@astrojs/preact':
|
||||
|
@ -7400,6 +7418,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-arm64@0.17.18:
|
||||
resolution: {integrity: sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-arm@0.15.18:
|
||||
resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7417,6 +7443,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-arm@0.17.18:
|
||||
resolution: {integrity: sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-x64@0.17.12:
|
||||
resolution: {integrity: sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7425,6 +7459,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-x64@0.17.18:
|
||||
resolution: {integrity: sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-arm64@0.17.12:
|
||||
resolution: {integrity: sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7433,6 +7475,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-arm64@0.17.18:
|
||||
resolution: {integrity: sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-x64@0.17.12:
|
||||
resolution: {integrity: sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7441,6 +7491,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-x64@0.17.18:
|
||||
resolution: {integrity: sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-arm64@0.17.12:
|
||||
resolution: {integrity: sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7449,6 +7507,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-arm64@0.17.18:
|
||||
resolution: {integrity: sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-x64@0.17.12:
|
||||
resolution: {integrity: sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7457,6 +7523,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-x64@0.17.18:
|
||||
resolution: {integrity: sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm64@0.17.12:
|
||||
resolution: {integrity: sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7465,6 +7539,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm64@0.17.18:
|
||||
resolution: {integrity: sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm@0.17.12:
|
||||
resolution: {integrity: sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7473,6 +7555,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm@0.17.18:
|
||||
resolution: {integrity: sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ia32@0.17.12:
|
||||
resolution: {integrity: sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7481,6 +7571,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ia32@0.17.18:
|
||||
resolution: {integrity: sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-loong64@0.15.18:
|
||||
resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7498,6 +7596,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-loong64@0.17.18:
|
||||
resolution: {integrity: sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-mips64el@0.17.12:
|
||||
resolution: {integrity: sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7506,6 +7612,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-mips64el@0.17.18:
|
||||
resolution: {integrity: sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ppc64@0.17.12:
|
||||
resolution: {integrity: sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7514,6 +7628,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ppc64@0.17.18:
|
||||
resolution: {integrity: sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-riscv64@0.17.12:
|
||||
resolution: {integrity: sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7522,6 +7644,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-riscv64@0.17.18:
|
||||
resolution: {integrity: sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-s390x@0.17.12:
|
||||
resolution: {integrity: sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7530,6 +7660,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-s390x@0.17.18:
|
||||
resolution: {integrity: sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-x64@0.17.12:
|
||||
resolution: {integrity: sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7538,6 +7676,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-x64@0.17.18:
|
||||
resolution: {integrity: sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/netbsd-x64@0.17.12:
|
||||
resolution: {integrity: sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7546,6 +7692,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/netbsd-x64@0.17.18:
|
||||
resolution: {integrity: sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/openbsd-x64@0.17.12:
|
||||
resolution: {integrity: sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7554,6 +7708,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/openbsd-x64@0.17.18:
|
||||
resolution: {integrity: sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/sunos-x64@0.17.12:
|
||||
resolution: {integrity: sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7562,6 +7724,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/sunos-x64@0.17.18:
|
||||
resolution: {integrity: sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-arm64@0.17.12:
|
||||
resolution: {integrity: sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7570,6 +7740,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-arm64@0.17.18:
|
||||
resolution: {integrity: sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-ia32@0.17.12:
|
||||
resolution: {integrity: sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7578,6 +7756,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-ia32@0.17.18:
|
||||
resolution: {integrity: sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-x64@0.17.12:
|
||||
resolution: {integrity: sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -7586,6 +7772,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-x64@0.17.18:
|
||||
resolution: {integrity: sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.38.0):
|
||||
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
@ -11335,6 +11529,35 @@ packages:
|
|||
'@esbuild/win32-ia32': 0.17.12
|
||||
'@esbuild/win32-x64': 0.17.12
|
||||
|
||||
/esbuild@0.17.18:
|
||||
resolution: {integrity: sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@esbuild/android-arm': 0.17.18
|
||||
'@esbuild/android-arm64': 0.17.18
|
||||
'@esbuild/android-x64': 0.17.18
|
||||
'@esbuild/darwin-arm64': 0.17.18
|
||||
'@esbuild/darwin-x64': 0.17.18
|
||||
'@esbuild/freebsd-arm64': 0.17.18
|
||||
'@esbuild/freebsd-x64': 0.17.18
|
||||
'@esbuild/linux-arm': 0.17.18
|
||||
'@esbuild/linux-arm64': 0.17.18
|
||||
'@esbuild/linux-ia32': 0.17.18
|
||||
'@esbuild/linux-loong64': 0.17.18
|
||||
'@esbuild/linux-mips64el': 0.17.18
|
||||
'@esbuild/linux-ppc64': 0.17.18
|
||||
'@esbuild/linux-riscv64': 0.17.18
|
||||
'@esbuild/linux-s390x': 0.17.18
|
||||
'@esbuild/linux-x64': 0.17.18
|
||||
'@esbuild/netbsd-x64': 0.17.18
|
||||
'@esbuild/openbsd-x64': 0.17.18
|
||||
'@esbuild/sunos-x64': 0.17.18
|
||||
'@esbuild/win32-arm64': 0.17.18
|
||||
'@esbuild/win32-ia32': 0.17.18
|
||||
'@esbuild/win32-x64': 0.17.18
|
||||
|
||||
/escalade@3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -17392,7 +17615,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@types/node': 18.16.3
|
||||
esbuild: 0.17.12
|
||||
esbuild: 0.17.18
|
||||
postcss: 8.4.23
|
||||
rollup: 3.21.8
|
||||
sass: 1.52.2
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import esbuild from 'esbuild';
|
||||
import { red } from 'kleur/colors';
|
||||
import glob from 'tiny-glob';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
@ -39,11 +40,40 @@ export default async function prebuild(...args) {
|
|||
}
|
||||
|
||||
async function prebuildFile(filepath) {
|
||||
const tscode = await fs.promises.readFile(filepath, 'utf-8');
|
||||
const esbuildresult = await esbuild.transform(tscode, {
|
||||
loader: 'ts',
|
||||
let tscode = await fs.promises.readFile(filepath, 'utf-8');
|
||||
// If we're bundling a client directive, modify the code to match `packages/astro/src/core/client-directive/build.ts`.
|
||||
// If updating this code, make sure to also update that file.
|
||||
if (filepath.includes(`runtime${path.sep}client`)) {
|
||||
// `export default xxxDirective` is a convention used in the current client directives that we use
|
||||
// to make sure we bundle this right. We'll error below if this convention isn't followed.
|
||||
const newTscode = tscode.replace(
|
||||
/export default (.*?)Directive/,
|
||||
(_, name) =>
|
||||
`(self.Astro || (self.Astro = {})).${name} = ${name}Directive;window.dispatchEvent(new Event('astro:${name}'))`
|
||||
);
|
||||
if (newTscode === tscode) {
|
||||
console.error(
|
||||
red(
|
||||
`${filepath} doesn't follow the \`export default xxxDirective\` convention. The prebuilt output may be wrong. ` +
|
||||
`For more information, check out ${fileURLToPath(import.meta.url)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
tscode = newTscode;
|
||||
}
|
||||
const esbuildresult = await esbuild.build({
|
||||
stdin: {
|
||||
contents: tscode,
|
||||
resolveDir: path.dirname(filepath),
|
||||
loader: 'ts',
|
||||
sourcefile: filepath,
|
||||
},
|
||||
format: 'iife',
|
||||
minify,
|
||||
bundle: true,
|
||||
write: false,
|
||||
});
|
||||
const code = esbuildresult.outputFiles[0].text.trim();
|
||||
const rootURL = new URL('../../', import.meta.url);
|
||||
const rel = path.relative(fileURLToPath(rootURL), filepath);
|
||||
const mod = `/**
|
||||
|
@ -52,7 +82,7 @@ export default async function prebuild(...args) {
|
|||
* to generate this file.
|
||||
*/
|
||||
|
||||
export default \`${escapeTemplateLiterals(esbuildresult.code.trim())}\`;`;
|
||||
export default \`${escapeTemplateLiterals(code)}\`;`;
|
||||
const url = getPrebuildURL(filepath);
|
||||
await fs.promises.writeFile(url, mod, 'utf-8');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue