Skip to content

Commit

Permalink
Prevent astro:content from depending on Node builtins (#6537)
Browse files Browse the repository at this point in the history
* Prevent astro:content from depending on Node builtins

* Right file

* Move the plugin into test-plugins.js
  • Loading branch information
matthewp committed Mar 13, 2023
1 parent 87d5e96 commit 6a7cf07
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-planets-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Prevent astro:content from depending on Node builtins
2 changes: 2 additions & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
28 changes: 28 additions & 0 deletions packages/astro/src/content/runtime-assets.ts
Original file line number Diff line number Diff line change
@@ -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 };
}
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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 };
}
9 changes: 9 additions & 0 deletions packages/astro/src/content/template/virtual-mod-assets.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {
createImage
} from 'astro/content/runtime-assets';

const assetsDir = '@@ASSETS_DIR@@';

export const image = createImage({
assetsDir,
});
8 changes: 1 addition & 7 deletions packages/astro/src/content/template/virtual-mod.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import {
createCollectionToGlobResultMap,
createGetCollection,
createGetEntryBySlug,
createImage,
} from 'astro/content/internal';
} from 'astro/content/runtime';

export { z } from 'astro/zod';

Expand All @@ -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 },
Expand All @@ -40,7 +38,3 @@ export const getEntryBySlug = createGetEntryBySlug({
getCollection,
collectionToRenderEntryMap,
});

export const image = createImage({
assetsDir,
});
2 changes: 2 additions & 0 deletions packages/astro/src/content/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export type ContentPaths = {
cacheDir: URL;
typesTemplate: URL;
virtualModTemplate: URL;
virtualAssetsModTemplate: URL;
config: {
exists: boolean;
url: URL;
Expand All @@ -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,
};
}
Expand Down
9 changes: 7 additions & 2 deletions packages/astro/src/content/vite-plugin-content-virtual-mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -53,7 +58,7 @@ export function astroContentVirtualModPlugin({
load(id) {
if (id === astroContentVirtualModuleId) {
return {
code: virtualModContents,
code: allContents,
};
}
},
Expand Down
6 changes: 6 additions & 0 deletions packages/astro/test/content-collections.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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();
Expand Down
17 changes: 17 additions & 0 deletions packages/astro/test/test-plugins.js
Original file line number Diff line number Diff line change
@@ -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}`)
}
});
}
};
}

0 comments on commit 6a7cf07

Please sign in to comment.