Fix memleak when project path contains '.md' (#5264)
This commit is contained in:
parent
78145c7de5
commit
0d27c4a2b6
12 changed files with 94 additions and 19 deletions
5
.changeset/clever-files-wash.md
Normal file
5
.changeset/clever-files-wash.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixed memleak caused by project dir names containing '.md' or '.mdx'
|
|
@ -19,19 +19,10 @@ export function isURL(value: unknown): value is URL {
|
||||||
return Object.prototype.toString.call(value) === '[object URL]';
|
return Object.prototype.toString.call(value) === '[object URL]';
|
||||||
}
|
}
|
||||||
/** Check if a file is a markdown file based on its extension */
|
/** Check if a file is a markdown file based on its extension */
|
||||||
export function isMarkdownFile(
|
export function isMarkdownFile(fileId: string, option?: { suffix?: string }): boolean {
|
||||||
fileId: string,
|
const _suffix = option?.suffix ?? '';
|
||||||
option: { criteria: 'endsWith' | 'includes'; suffix?: string }
|
|
||||||
): boolean {
|
|
||||||
const _suffix = option.suffix ?? '';
|
|
||||||
if (option.criteria === 'endsWith') {
|
|
||||||
for (let markdownFileExtension of SUPPORTED_MARKDOWN_FILE_EXTENSIONS) {
|
|
||||||
if (fileId.endsWith(`${markdownFileExtension}${_suffix}`)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (let markdownFileExtension of SUPPORTED_MARKDOWN_FILE_EXTENSIONS) {
|
for (let markdownFileExtension of SUPPORTED_MARKDOWN_FILE_EXTENSIONS) {
|
||||||
if (fileId.includes(`${markdownFileExtension}${_suffix}`)) return true;
|
if (fileId.endsWith(`${markdownFileExtension}${_suffix}`)) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default function astro(_opts: AstroPluginOptions): Plugin {
|
||||||
name: 'astro:postprocess',
|
name: 'astro:postprocess',
|
||||||
async transform(code, id) {
|
async transform(code, id) {
|
||||||
// Currently only supported in ".astro" and ".md" (or any alternative markdown file extension like `.markdown`) files
|
// Currently only supported in ".astro" and ".md" (or any alternative markdown file extension like `.markdown`) files
|
||||||
if (!id.endsWith('.astro') && !isMarkdownFile(id, { criteria: 'endsWith' })) {
|
if (!id.endsWith('.astro') && !isMarkdownFile(id)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ export default function jsx({ settings, logging }: AstroPluginJSXOptions): Plugi
|
||||||
|
|
||||||
const { mode } = viteConfig;
|
const { mode } = viteConfig;
|
||||||
// Shortcut: only use Astro renderer for MD and MDX files
|
// Shortcut: only use Astro renderer for MD and MDX files
|
||||||
if (id.includes('.mdx') || isMarkdownFile(id, { criteria: 'includes' })) {
|
if (id.endsWith('.mdx')) {
|
||||||
const { code: jsxCode } = await esbuild.transform(code, {
|
const { code: jsxCode } = await esbuild.transform(code, {
|
||||||
loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
|
loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader,
|
||||||
jsx: 'preserve',
|
jsx: 'preserve',
|
||||||
|
|
|
@ -90,7 +90,7 @@ export default function markdown({ settings }: AstroPluginOptions): Plugin {
|
||||||
// Resolve any .md (or alternative extensions of markdown files like .markdown) files with the `?content` cache buster. This should only come from
|
// Resolve any .md (or alternative extensions of markdown files like .markdown) files with the `?content` cache buster. This should only come from
|
||||||
// an already-resolved JS module wrapper. Needed to prevent infinite loops in Vite.
|
// an already-resolved JS module wrapper. Needed to prevent infinite loops in Vite.
|
||||||
// Unclear if this is expected or if cache busting is just working around a Vite bug.
|
// Unclear if this is expected or if cache busting is just working around a Vite bug.
|
||||||
if (isMarkdownFile(id, { criteria: 'endsWith', suffix: MARKDOWN_CONTENT_FLAG })) {
|
if (isMarkdownFile(id, { suffix: MARKDOWN_CONTENT_FLAG })) {
|
||||||
const resolvedId = await this.resolve(id, importer, { skipSelf: true, ...options });
|
const resolvedId = await this.resolve(id, importer, { skipSelf: true, ...options });
|
||||||
return resolvedId?.id.replace(MARKDOWN_CONTENT_FLAG, '');
|
return resolvedId?.id.replace(MARKDOWN_CONTENT_FLAG, '');
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ export default function markdown({ settings }: AstroPluginOptions): Plugin {
|
||||||
// that defers the markdown -> HTML rendering until it is needed. This is especially useful
|
// that defers the markdown -> HTML rendering until it is needed. This is especially useful
|
||||||
// when fetching and then filtering many markdown files, like with import.meta.glob() or Astro.glob().
|
// when fetching and then filtering many markdown files, like with import.meta.glob() or Astro.glob().
|
||||||
// Otherwise, resolve directly to the actual component.
|
// Otherwise, resolve directly to the actual component.
|
||||||
if (isMarkdownFile(id, { criteria: 'endsWith' }) && !isRootImport(importer)) {
|
if (isMarkdownFile(id) && !isRootImport(importer)) {
|
||||||
const resolvedId = await this.resolve(id, importer, { skipSelf: true, ...options });
|
const resolvedId = await this.resolve(id, importer, { skipSelf: true, ...options });
|
||||||
if (resolvedId) {
|
if (resolvedId) {
|
||||||
return resolvedId.id + MARKDOWN_IMPORT_FLAG;
|
return resolvedId.id + MARKDOWN_IMPORT_FLAG;
|
||||||
|
@ -111,7 +111,7 @@ export default function markdown({ settings }: AstroPluginOptions): Plugin {
|
||||||
// A markdown file has been imported via ESM!
|
// A markdown file has been imported via ESM!
|
||||||
// Return the file's JS representation, including all Markdown
|
// Return the file's JS representation, including all Markdown
|
||||||
// frontmatter and a deferred `import() of the compiled markdown content.
|
// frontmatter and a deferred `import() of the compiled markdown content.
|
||||||
if (isMarkdownFile(id, { criteria: 'endsWith', suffix: MARKDOWN_IMPORT_FLAG })) {
|
if (isMarkdownFile(id, { suffix: MARKDOWN_IMPORT_FLAG })) {
|
||||||
const { fileId, fileUrl } = getFileInfo(id, config);
|
const { fileId, fileUrl } = getFileInfo(id, config);
|
||||||
|
|
||||||
const source = await fs.promises.readFile(fileId, 'utf8');
|
const source = await fs.promises.readFile(fileId, 'utf8');
|
||||||
|
@ -151,7 +151,7 @@ export default function markdown({ settings }: AstroPluginOptions): Plugin {
|
||||||
// A markdown file is being rendered! This markdown file was either imported
|
// A markdown file is being rendered! This markdown file was either imported
|
||||||
// directly as a page in Vite, or it was a deferred render from a JS module.
|
// directly as a page in Vite, or it was a deferred render from a JS module.
|
||||||
// This returns the compiled markdown -> astro component that renders to HTML.
|
// This returns the compiled markdown -> astro component that renders to HTML.
|
||||||
if (isMarkdownFile(id, { criteria: 'endsWith' })) {
|
if (isMarkdownFile(id)) {
|
||||||
const filename = normalizeFilename(id);
|
const filename = normalizeFilename(id);
|
||||||
const source = await fs.promises.readFile(filename, 'utf8');
|
const source = await fs.promises.readFile(filename, 'utf8');
|
||||||
const renderOpts = config.markdown;
|
const renderOpts = config.markdown;
|
||||||
|
|
|
@ -59,7 +59,7 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu
|
||||||
// passing to the transform hook. This lets us get the truly raw value
|
// passing to the transform hook. This lets us get the truly raw value
|
||||||
// to escape "import.meta.env" ourselves.
|
// to escape "import.meta.env" ourselves.
|
||||||
async load(id) {
|
async load(id) {
|
||||||
if (isMarkdownFile(id, { criteria: 'endsWith' })) {
|
if (isMarkdownFile(id)) {
|
||||||
const { fileId, fileUrl } = getFileInfo(id, settings.config);
|
const { fileId, fileUrl } = getFileInfo(id, settings.config);
|
||||||
const rawFile = await fs.promises.readFile(fileId, 'utf-8');
|
const rawFile = await fs.promises.readFile(fileId, 'utf-8');
|
||||||
const raw = safeMatter(rawFile, id);
|
const raw = safeMatter(rawFile, id);
|
||||||
|
|
7
packages/astro/test/fixtures/impostor-mdx-file/astro.config.mjs
vendored
Normal file
7
packages/astro/test/fixtures/impostor-mdx-file/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import react from '@astrojs/react';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [react()],
|
||||||
|
});
|
11
packages/astro/test/fixtures/impostor-mdx-file/package.json
vendored
Normal file
11
packages/astro/test/fixtures/impostor-mdx-file/package.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "@test/impostor-md-file",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/react": "workspace:*",
|
||||||
|
"astro": "workspace:*",
|
||||||
|
"react": "^18.1.0",
|
||||||
|
"react-dom": "^18.1.0"
|
||||||
|
}
|
||||||
|
}
|
6
packages/astro/test/fixtures/impostor-mdx-file/src/components/Foo.mdx.jsx
vendored
Normal file
6
packages/astro/test/fixtures/impostor-mdx-file/src/components/Foo.mdx.jsx
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import {useState} from "react";
|
||||||
|
|
||||||
|
export default function Foo() {
|
||||||
|
const [bar, setBar] = useState("Baz");
|
||||||
|
return <div className="main-component">{bar}</div>
|
||||||
|
}
|
12
packages/astro/test/fixtures/impostor-mdx-file/src/pages/index.astro
vendored
Normal file
12
packages/astro/test/fixtures/impostor-mdx-file/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
import Foo from '../components/Foo.mdx.jsx';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Head Stuff -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<Foo client:load />
|
||||||
|
</body>
|
||||||
|
</html>
|
31
packages/astro/test/impostor-mdx-file.js
Normal file
31
packages/astro/test/impostor-mdx-file.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { isWindows, loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
describe('Impostor MDX File', () => {
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/impostor-mdx-file/',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (isWindows) return;
|
||||||
|
|
||||||
|
describe('dev', () => {
|
||||||
|
/** @type {import('./test-utils').Fixture} */
|
||||||
|
let devServer;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
devServer = await fixture.startDevServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await devServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not crash when loading react component with .md or .mdx in name', async () => {
|
||||||
|
const result = await fixture.fetch('/').then((response) => response.text());
|
||||||
|
expect(result).to.contain('Baz');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1810,6 +1810,18 @@ importers:
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/impostor-mdx-file:
|
||||||
|
specifiers:
|
||||||
|
'@astrojs/react': workspace:*
|
||||||
|
astro: workspace:*
|
||||||
|
react: ^18.1.0
|
||||||
|
react-dom: ^18.1.0
|
||||||
|
dependencies:
|
||||||
|
'@astrojs/react': link:../../../../integrations/react
|
||||||
|
astro: link:../../..
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0_react@18.2.0
|
||||||
|
|
||||||
packages/astro/test/fixtures/integration-add-page-extension:
|
packages/astro/test/fixtures/integration-add-page-extension:
|
||||||
specifiers:
|
specifiers:
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
|
|
Loading…
Reference in a new issue