Prevent astro:content from depending on Node builtins (#6537)
* Prevent astro:content from depending on Node builtins * Right file * Move the plugin into test-plugins.js
This commit is contained in:
parent
87d5e96da4
commit
6a7cf0712d
10 changed files with 77 additions and 37 deletions
5
.changeset/four-planets-smoke.md
Normal file
5
.changeset/four-planets-smoke.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Prevent astro:content from depending on Node builtins
|
|
@ -53,6 +53,8 @@
|
|||
"./assets/services/sharp": "./dist/assets/services/sharp.js",
|
||||
"./assets/services/squoosh": "./dist/assets/services/squoosh.js",
|
||||
"./content/internal": "./dist/content/internal.js",
|
||||
"./content/runtime": "./dist/content/runtime.js",
|
||||
"./content/runtime-assets": "./dist/content/runtime-assets.js",
|
||||
"./debug": "./components/Debug.astro",
|
||||
"./internal/*": "./dist/runtime/server/*",
|
||||
"./package.json": "./package.json",
|
||||
|
|
28
packages/astro/src/content/runtime-assets.ts
Normal file
28
packages/astro/src/content/runtime-assets.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { z } from 'zod';
|
||||
import { imageMetadata, type Metadata } from '../assets/utils/metadata.js';
|
||||
|
||||
export function createImage(options: { assetsDir: string; relAssetsDir: string }) {
|
||||
return () => {
|
||||
if (options.assetsDir === 'undefined') {
|
||||
throw new Error('Enable `experimental.assets` in your Astro config to use image()');
|
||||
}
|
||||
|
||||
return z.string().transform(async (imagePath) => {
|
||||
const fullPath = new URL(imagePath, options.assetsDir);
|
||||
return await getImageMetadata(fullPath);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async function getImageMetadata(
|
||||
imagePath: URL
|
||||
): Promise<(Metadata & { __astro_asset: true }) | undefined> {
|
||||
const meta = await imageMetadata(imagePath);
|
||||
|
||||
if (!meta) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
delete meta.orientation;
|
||||
return { ...meta, __astro_asset: true };
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
import { z } from 'zod';
|
||||
import { imageMetadata, type Metadata } from '../assets/utils/metadata.js';
|
||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
import { prependForwardSlash } from '../core/path.js';
|
||||
|
||||
|
@ -199,29 +197,3 @@ async function render({
|
|||
remarkPluginFrontmatter: mod.frontmatter ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
export function createImage(options: { assetsDir: string; relAssetsDir: string }) {
|
||||
return () => {
|
||||
if (options.assetsDir === 'undefined') {
|
||||
throw new Error('Enable `experimental.assets` in your Astro config to use image()');
|
||||
}
|
||||
|
||||
return z.string().transform(async (imagePath) => {
|
||||
const fullPath = new URL(imagePath, options.assetsDir);
|
||||
return await getImageMetadata(fullPath);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async function getImageMetadata(
|
||||
imagePath: URL
|
||||
): Promise<(Metadata & { __astro_asset: true }) | undefined> {
|
||||
const meta = await imageMetadata(imagePath);
|
||||
|
||||
if (!meta) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
delete meta.orientation;
|
||||
return { ...meta, __astro_asset: true };
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import {
|
||||
createImage
|
||||
} from 'astro/content/runtime-assets';
|
||||
|
||||
const assetsDir = '@@ASSETS_DIR@@';
|
||||
|
||||
export const image = createImage({
|
||||
assetsDir,
|
||||
});
|
|
@ -3,8 +3,7 @@ import {
|
|||
createCollectionToGlobResultMap,
|
||||
createGetCollection,
|
||||
createGetEntryBySlug,
|
||||
createImage,
|
||||
} from 'astro/content/internal';
|
||||
} from 'astro/content/runtime';
|
||||
|
||||
export { z } from 'astro/zod';
|
||||
|
||||
|
@ -13,7 +12,6 @@ export function defineCollection(config) {
|
|||
}
|
||||
|
||||
const contentDir = '@@CONTENT_DIR@@';
|
||||
const assetsDir = '@@ASSETS_DIR@@';
|
||||
|
||||
const entryGlob = import.meta.glob('@@ENTRY_GLOB_PATH@@', {
|
||||
query: { astroContent: true },
|
||||
|
@ -40,7 +38,3 @@ export const getEntryBySlug = createGetEntryBySlug({
|
|||
getCollection,
|
||||
collectionToRenderEntryMap,
|
||||
});
|
||||
|
||||
export const image = createImage({
|
||||
assetsDir,
|
||||
});
|
||||
|
|
|
@ -334,6 +334,7 @@ export type ContentPaths = {
|
|||
cacheDir: URL;
|
||||
typesTemplate: URL;
|
||||
virtualModTemplate: URL;
|
||||
virtualAssetsModTemplate: URL;
|
||||
config: {
|
||||
exists: boolean;
|
||||
url: URL;
|
||||
|
@ -352,6 +353,7 @@ export function getContentPaths(
|
|||
assetsDir: new URL('./assets/', srcDir),
|
||||
typesTemplate: new URL('types.d.ts', templateDir),
|
||||
virtualModTemplate: new URL('virtual-mod.mjs', templateDir),
|
||||
virtualAssetsModTemplate: new URL('virtual-mod-assets.mjs', templateDir),
|
||||
config: configStats,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -36,11 +36,16 @@ export function astroContentVirtualModPlugin({
|
|||
const virtualModContents = fsMod
|
||||
.readFileSync(contentPaths.virtualModTemplate, 'utf-8')
|
||||
.replace('@@CONTENT_DIR@@', relContentDir)
|
||||
.replace('@@ASSETS_DIR@@', assetsDir)
|
||||
.replace('@@ENTRY_GLOB_PATH@@', entryGlob)
|
||||
.replace('@@RENDER_ENTRY_GLOB_PATH@@', entryGlob);
|
||||
const virtualAssetsModContents = fsMod
|
||||
.readFileSync(contentPaths.virtualAssetsModTemplate, 'utf-8')
|
||||
.replace('@@ASSETS_DIR@@', assetsDir);
|
||||
|
||||
const astroContentVirtualModuleId = '\0' + VIRTUAL_MODULE_ID;
|
||||
const allContents = settings.config.experimental.assets ?
|
||||
(virtualModContents + virtualAssetsModContents) :
|
||||
virtualModContents;
|
||||
|
||||
return {
|
||||
name: 'astro-content-virtual-mod-plugin',
|
||||
|
@ -53,7 +58,7 @@ export function astroContentVirtualModPlugin({
|
|||
load(id) {
|
||||
if (id === astroContentVirtualModuleId) {
|
||||
return {
|
||||
code: virtualModContents,
|
||||
code: allContents,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as cheerio from 'cheerio';
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
import testAdapter from './test-adapter.js';
|
||||
import { preventNodeBuiltinDependencyPlugin } from './test-plugins.js';
|
||||
|
||||
describe('Content Collections', () => {
|
||||
describe('Query', () => {
|
||||
|
@ -222,6 +223,11 @@ describe('Content Collections', () => {
|
|||
root: './fixtures/content-ssr-integration/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
vite: {
|
||||
plugins: [
|
||||
preventNodeBuiltinDependencyPlugin()
|
||||
]
|
||||
}
|
||||
});
|
||||
await fixture.build();
|
||||
app = await fixture.loadTestAdapterApp();
|
||||
|
|
17
packages/astro/test/test-plugins.js
Normal file
17
packages/astro/test/test-plugins.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
export function preventNodeBuiltinDependencyPlugin() {
|
||||
// Verifies that `astro:content` does not have a hard dependency on Node builtins.
|
||||
// This is to verify it will run on Cloudflare and Deno
|
||||
return {
|
||||
name: 'verify-no-node-stuff',
|
||||
generateBundle() {
|
||||
const nodeModules = ['node:fs', 'node:url', 'node:worker_threads', 'node:path'];
|
||||
nodeModules.forEach(name => {
|
||||
const mod = this.getModuleInfo(name);
|
||||
if(mod) {
|
||||
throw new Error(`Node builtins snuck in: ${name}`)
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue