diff --git a/.changeset/quick-wolves-drive.md b/.changeset/quick-wolves-drive.md new file mode 100644 index 000000000..73c44265c --- /dev/null +++ b/.changeset/quick-wolves-drive.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Support content collections with % in filename diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 046b73b1c..b50c597fd 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -106,7 +106,8 @@ export async function createContentTypesGenerator({ }); for (const entry of globResult) { - const entryURL = new URL(entry.path, contentPaths.contentDir); + const fullPath = path.join(fileURLToPath(contentPaths.contentDir), entry.path); + const entryURL = pathToFileURL(fullPath); if (entryURL.href.startsWith(contentPaths.config.url.href)) continue; if (entry.dirent.isFile()) { events.push({ diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 149b6760d..38bedb469 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -4,7 +4,7 @@ import glob from 'fast-glob'; import { bgGreen, bgMagenta, black, dim } from 'kleur/colors'; import fs from 'node:fs'; import path, { extname } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import * as vite from 'vite'; import type { RouteData } from '../../@types/astro.js'; import { @@ -186,6 +186,17 @@ async function ssrBuild( const sanitizedName = name.split('.')[0]; return `chunks/${sanitizedName}_[hash].mjs`; } + // Detect if the chunk name has as % sign that is not encoded. + // This is borrowed from Node core: https://github.com/nodejs/node/blob/3838b579e44bf0c2db43171c3ce0da51eb6b05d5/lib/internal/url.js#L1382-L1391 + // We do this because you cannot import a module with this character in it. + for(let i = 0; i < name.length; i++) { + if(name[i] === '%') { + const third = name.codePointAt(i + 2)! | 0x20; + if (name[i + 1] !== '2' || third !== 102) { + return `chunks/${name.replace(/%/g, '_percent_')}_[hash].mjs`; + } + } + } return `chunks/[name]_[hash].mjs`; }, assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`, @@ -313,7 +324,8 @@ async function runPostBuildHooks( ? build.server : build.client : config.outDir; - const fileURL = new URL(fileName, root); + const fullPath = path.join(fileURLToPath(root), fileName); + const fileURL = pathToFileURL(fullPath); await fs.promises.mkdir(new URL('./', fileURL), { recursive: true }); await fs.promises.writeFile(fileURL, mutation.code, 'utf-8'); } diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index dfc0bc0e0..6422f7305 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -6,7 +6,7 @@ import { import matter from 'gray-matter'; import fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import type { Plugin } from 'vite'; import { normalizePath } from 'vite'; import type { AstroSettings } from '../@types/astro.js'; @@ -76,9 +76,11 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug const rawFile = await fs.promises.readFile(fileId, 'utf-8'); const raw = safeMatter(rawFile, id); + const fileURL = pathToFileURL(fileId); + const renderResult = await processor .render(raw.content, { - fileURL: new URL(`file://${fileId}`), + fileURL, frontmatter: raw.data, }) .catch((err) => { diff --git a/packages/astro/test/content-collections.test.js b/packages/astro/test/content-collections.test.js index 082a7c0eb..699254991 100644 --- a/packages/astro/test/content-collections.test.js +++ b/packages/astro/test/content-collections.test.js @@ -54,12 +54,13 @@ describe('Content Collections', () => { const ids = json.withSchemaConfig.map((item) => item.id); const publishedDates = json.withSchemaConfig.map((item) => item.data.publishedAt); - expect(ids).to.deep.equal(['one.md', 'three.md', 'two.md']); + expect(ids).to.deep.equal(['four%.md', 'one.md', 'three.md', 'two.md']); expect(publishedDates.every((date) => date instanceof Date)).to.equal( true, 'Not all publishedAt dates are Date objects' ); expect(publishedDates.map((date) => date.toISOString())).to.deep.equal([ + '2021-01-01T00:00:00.000Z', '2021-01-01T00:00:00.000Z', '2021-01-03T00:00:00.000Z', '2021-01-02T00:00:00.000Z', diff --git a/packages/astro/test/fixtures/content-collections/src/content/with-schema-config/four%.md b/packages/astro/test/fixtures/content-collections/src/content/with-schema-config/four%.md new file mode 100644 index 000000000..bad429c8c --- /dev/null +++ b/packages/astro/test/fixtures/content-collections/src/content/with-schema-config/four%.md @@ -0,0 +1,8 @@ +--- +title: Four +description: The forth page +lang: en +publishedAt: 2021-01-01 +--- + +# It's the forth page, fancy!