summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Happydev <81974850+MoustaphaDev@users.noreply.github.com> 2023-02-13 22:19:16 +0000
committerGravatar GitHub <noreply@github.com> 2023-02-13 22:19:16 +0000
commitc397be324f97bb9700da8cd6d845470530b7d18c (patch)
tree37253c60b0e9b2997d7e94b8754878cf91ec6498
parent27a0b6339b0eb1af0e31f163a0b4c96334e9575c (diff)
downloadastro-c397be324f97bb9700da8cd6d845470530b7d18c.tar.gz
astro-c397be324f97bb9700da8cd6d845470530b7d18c.tar.zst
astro-c397be324f97bb9700da8cd6d845470530b7d18c.zip
fix: add support for `js/mjs` file extensions for Content Collections config file (#6229)
* test: add fixture * test: add test case * test: fix tests * feat: support mjs/ js file extensions for cc config * chore: sync lockfile * test: make assertion more specific * test: make template minimal * chore: add changeset * feat: add warning when `allowJs` is `false` * improve warning * extract tsconfig loader to another function * rename to more descriptive variable * apply review suggestion Co-authored-by: Ben Holmes <hey@bholmes.dev> --------- Co-authored-by: Ben Holmes <hey@bholmes.dev>
-rw-r--r--.changeset/three-peaches-guess.md5
-rw-r--r--packages/astro/src/content/server-listeners.ts63
-rw-r--r--packages/astro/src/content/types-generator.ts8
-rw-r--r--packages/astro/src/content/utils.ts37
-rw-r--r--packages/astro/src/content/vite-plugin-content-imports.ts2
-rw-r--r--packages/astro/test/content-collections.test.js14
-rw-r--r--packages/astro/test/fixtures/content-collections-with-config-mjs/package.json9
-rw-r--r--packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/blog/introduction.md5
-rw-r--r--packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/config.mjs11
-rw-r--r--packages/astro/test/fixtures/content-collections-with-config-mjs/src/pages/index.astro5
-rw-r--r--packages/astro/test/units/content-collections/get-entry-type.test.js7
-rw-r--r--pnpm-lock.yaml8
12 files changed, 151 insertions, 23 deletions
diff --git a/.changeset/three-peaches-guess.md b/.changeset/three-peaches-guess.md
new file mode 100644
index 000000000..c050e216c
--- /dev/null
+++ b/.changeset/three-peaches-guess.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Add support for `.js/.mjs` file extensions for Content Collections configuration file. \ No newline at end of file
diff --git a/packages/astro/src/content/server-listeners.ts b/packages/astro/src/content/server-listeners.ts
index f0513d07f..e204d893d 100644
--- a/packages/astro/src/content/server-listeners.ts
+++ b/packages/astro/src/content/server-listeners.ts
@@ -1,12 +1,14 @@
-import { cyan } from 'kleur/colors';
+import { bold, cyan } from 'kleur/colors';
import type fsMod from 'node:fs';
-import { pathToFileURL } from 'node:url';
+import { fileURLToPath, pathToFileURL } from 'node:url';
import type { ViteDevServer } from 'vite';
import type { AstroSettings } from '../@types/astro.js';
-import { info, LogOptions } from '../core/logger/core.js';
+import { info, LogOptions, warn } from '../core/logger/core.js';
import { appendForwardSlash } from '../core/path.js';
import { createContentTypesGenerator } from './types-generator.js';
-import { getContentPaths, globalContentConfigObserver } from './utils.js';
+import { ContentPaths, getContentPaths, globalContentConfigObserver } from './utils.js';
+import { loadTSConfig } from '../core/config/tsconfig.js';
+import path from 'node:path';
interface ContentServerListenerParams {
fs: typeof fsMod;
@@ -21,7 +23,10 @@ export async function attachContentServerListeners({
logging,
settings,
}: ContentServerListenerParams) {
- const contentPaths = getContentPaths(settings.config);
+ const contentPaths = getContentPaths(settings.config, fs);
+
+ const maybeTsConfigStats = getTSConfigStatsWhenAllowJsFalse({ contentPaths, settings });
+ if (maybeTsConfigStats) warnAllowJsIsFalse({ ...maybeTsConfigStats, logging });
if (fs.existsSync(contentPaths.contentDir)) {
info(
@@ -71,3 +76,51 @@ export async function attachContentServerListeners({
);
}
}
+
+function warnAllowJsIsFalse({
+ logging,
+ tsConfigFileName,
+ contentConfigFileName,
+}: {
+ logging: LogOptions;
+ tsConfigFileName: string;
+ contentConfigFileName: string;
+}) {
+ if (!['info', 'warn'].includes(logging.level))
+ warn(
+ logging,
+ 'content',
+ `Make sure you have the ${bold('allowJs')} compiler option set to ${bold(
+ 'true'
+ )} in your ${bold(tsConfigFileName)} file to have autocompletion in your ${bold(
+ contentConfigFileName
+ )} file.
+See ${bold('https://www.typescriptlang.org/tsconfig#allowJs')} for more information.
+ `
+ );
+}
+
+function getTSConfigStatsWhenAllowJsFalse({
+ contentPaths,
+ settings,
+}: {
+ contentPaths: ContentPaths;
+ settings: AstroSettings;
+}) {
+ const isContentConfigJsFile = ['.js', '.mjs'].some((ext) =>
+ contentPaths.config.url.pathname.endsWith(ext)
+ );
+ if (!isContentConfigJsFile) return;
+
+ const inputConfig = loadTSConfig(fileURLToPath(settings.config.root), false);
+ const tsConfigFileName = inputConfig.exists && inputConfig.path.split(path.sep).pop();
+ if (!tsConfigFileName) return;
+
+ const contentConfigFileName = contentPaths.config.url.pathname.split(path.sep).pop()!;
+ const allowJSOption = inputConfig?.config?.compilerOptions?.allowJs;
+ const hasAllowJs =
+ allowJSOption === true || (tsConfigFileName === 'jsconfig.json' && allowJSOption !== false);
+ if (hasAllowJs) return;
+
+ return { tsConfigFileName, contentConfigFileName };
+}
diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts
index e68634f2f..a7899e923 100644
--- a/packages/astro/src/content/types-generator.ts
+++ b/packages/astro/src/content/types-generator.ts
@@ -51,7 +51,7 @@ export async function createContentTypesGenerator({
viteServer,
}: CreateContentGeneratorParams) {
const contentTypes: ContentTypes = {};
- const contentPaths = getContentPaths(settings.config);
+ const contentPaths = getContentPaths(settings.config, fs);
let events: Promise<{ shouldGenerateTypes: boolean; error?: Error }>[] = [];
let debounceTimeout: NodeJS.Timeout | undefined;
@@ -65,7 +65,7 @@ export async function createContentTypesGenerator({
return { typesGenerated: false, reason: 'no-content-dir' };
}
- events.push(handleEvent({ name: 'add', entry: contentPaths.config }, { logLevel: 'warn' }));
+ events.push(handleEvent({ name: 'add', entry: contentPaths.config.url }, { logLevel: 'warn' }));
const globResult = await glob('**', {
cwd: fileURLToPath(contentPaths.contentDir),
fs: {
@@ -77,7 +77,7 @@ export async function createContentTypesGenerator({
.map((e) => new URL(e, contentPaths.contentDir))
.filter(
// Config loading handled first. Avoid running twice.
- (e) => !e.href.startsWith(contentPaths.config.href)
+ (e) => !e.href.startsWith(contentPaths.config.url.href)
);
for (const entry of entries) {
events.push(handleEvent({ name: 'add', entry }, { logLevel: 'warn' }));
@@ -331,7 +331,7 @@ async function writeContentFiles({
}
let configPathRelativeToCacheDir = normalizePath(
- path.relative(contentPaths.cacheDir.pathname, contentPaths.config.pathname)
+ path.relative(contentPaths.cacheDir.pathname, contentPaths.config.url.pathname)
);
if (!isRelativePath(configPathRelativeToCacheDir))
configPathRelativeToCacheDir = './' + configPathRelativeToCacheDir;
diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts
index a800568de..70771f2dc 100644
--- a/packages/astro/src/content/utils.ts
+++ b/packages/astro/src/content/utils.ts
@@ -1,6 +1,6 @@
import { slug as githubSlug } from 'github-slugger';
import matter from 'gray-matter';
-import type fsMod from 'node:fs';
+import fsMod from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { ErrorPayload as ViteErrorPayload, normalizePath, ViteDevServer } from 'vite';
@@ -169,7 +169,7 @@ export function getEntryType(
return 'ignored';
} else if ((contentFileExts as readonly string[]).includes(ext)) {
return 'content';
- } else if (fileUrl.href === paths.config.href) {
+ } else if (fileUrl.href === paths.config.url.href) {
return 'config';
} else {
return 'unsupported';
@@ -250,13 +250,13 @@ export async function loadContentConfig({
settings: AstroSettings;
viteServer: ViteDevServer;
}): Promise<ContentConfig | undefined> {
- const contentPaths = getContentPaths(settings.config);
+ const contentPaths = getContentPaths(settings.config, fs);
let unparsedConfig;
- if (!fs.existsSync(contentPaths.config)) {
+ if (!contentPaths.config.exists) {
return undefined;
}
try {
- const configPathname = fileURLToPath(contentPaths.config);
+ const configPathname = fileURLToPath(contentPaths.config.url);
unparsedConfig = await viteServer.ssrLoadModule(configPathname);
} catch (e) {
throw e;
@@ -313,19 +313,34 @@ export type ContentPaths = {
cacheDir: URL;
typesTemplate: URL;
virtualModTemplate: URL;
- config: URL;
+ config: {
+ exists: boolean;
+ url: URL;
+ };
};
-export function getContentPaths({
- srcDir,
- root,
-}: Pick<AstroConfig, 'root' | 'srcDir'>): ContentPaths {
+export function getContentPaths(
+ { srcDir, root }: Pick<AstroConfig, 'root' | 'srcDir'>,
+ fs: typeof fsMod = fsMod
+): ContentPaths {
+ const configStats = search(fs, srcDir);
const templateDir = new URL('../../src/content/template/', import.meta.url);
return {
cacheDir: new URL('.astro/', root),
contentDir: new URL('./content/', srcDir),
typesTemplate: new URL('types.d.ts', templateDir),
virtualModTemplate: new URL('virtual-mod.mjs', templateDir),
- config: new URL('./content/config.ts', srcDir),
+ config: configStats,
};
}
+function search(fs: typeof fsMod, srcDir: URL) {
+ const paths = ['config.mjs', 'config.js', 'config.ts'].map(
+ (p) => new URL(`./content/${p}`, srcDir)
+ );
+ for (const file of paths) {
+ if (fs.existsSync(file)) {
+ return { exists: true, url: file };
+ }
+ }
+ return { exists: false, url: paths[0] };
+}
diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts
index 81dfd6335..d8075a1a1 100644
--- a/packages/astro/src/content/vite-plugin-content-imports.ts
+++ b/packages/astro/src/content/vite-plugin-content-imports.ts
@@ -30,7 +30,7 @@ export function astroContentImportPlugin({
fs: typeof fsMod;
settings: AstroSettings;
}): Plugin {
- const contentPaths = getContentPaths(settings.config);
+ const contentPaths = getContentPaths(settings.config, fs);
return {
name: 'astro:content-imports',
diff --git a/packages/astro/test/content-collections.test.js b/packages/astro/test/content-collections.test.js
index 4de67ca6e..67ef08235 100644
--- a/packages/astro/test/content-collections.test.js
+++ b/packages/astro/test/content-collections.test.js
@@ -199,6 +199,20 @@ describe('Content Collections', () => {
expect(error).to.be.null;
});
});
+ describe('With config.mjs', () => {
+ it("Errors when frontmatter doesn't match schema", async () => {
+ const fixture = await loadFixture({
+ root: './fixtures/content-collections-with-config-mjs/',
+ });
+ let error;
+ try {
+ await fixture.build();
+ } catch (e) {
+ error = e.message;
+ }
+ expect(error).to.include('"title" should be string, not number.');
+ });
+ });
describe('SSR integration', () => {
let app;
diff --git a/packages/astro/test/fixtures/content-collections-with-config-mjs/package.json b/packages/astro/test/fixtures/content-collections-with-config-mjs/package.json
new file mode 100644
index 000000000..eed4ebb90
--- /dev/null
+++ b/packages/astro/test/fixtures/content-collections-with-config-mjs/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/content-with-spaces-in-folder-name",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/mdx": "workspace:*"
+ }
+}
diff --git a/packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/blog/introduction.md b/packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/blog/introduction.md
new file mode 100644
index 000000000..c85be69f6
--- /dev/null
+++ b/packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/blog/introduction.md
@@ -0,0 +1,5 @@
+---
+title: 10000
+---
+
+# Hi there! \ No newline at end of file
diff --git a/packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/config.mjs b/packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/config.mjs
new file mode 100644
index 000000000..bb2c54aea
--- /dev/null
+++ b/packages/astro/test/fixtures/content-collections-with-config-mjs/src/content/config.mjs
@@ -0,0 +1,11 @@
+import { z, defineCollection } from 'astro:content';
+
+const blog = defineCollection({
+ schema: z.object({
+ title: z.string(),
+ }),
+});
+
+export const collections = {
+ blog
+}
diff --git a/packages/astro/test/fixtures/content-collections-with-config-mjs/src/pages/index.astro b/packages/astro/test/fixtures/content-collections-with-config-mjs/src/pages/index.astro
new file mode 100644
index 000000000..4152c0734
--- /dev/null
+++ b/packages/astro/test/fixtures/content-collections-with-config-mjs/src/pages/index.astro
@@ -0,0 +1,5 @@
+---
+import {getEntryBySlug} from "astro:content"
+const blogEntry = await getEntryBySlug("blog", "introduction");
+---
+{blogEntry.data.title} \ No newline at end of file
diff --git a/packages/astro/test/units/content-collections/get-entry-type.test.js b/packages/astro/test/units/content-collections/get-entry-type.test.js
index 3248a88f6..b9293d22d 100644
--- a/packages/astro/test/units/content-collections/get-entry-type.test.js
+++ b/packages/astro/test/units/content-collections/get-entry-type.test.js
@@ -5,7 +5,10 @@ import { fileURLToPath } from 'node:url';
describe('Content Collections - getEntryType', () => {
const contentDir = new URL('src/content/', import.meta.url);
const contentPaths = {
- config: new URL('src/content/config.ts', import.meta.url),
+ config: {
+ url: new URL('src/content/config.ts', import.meta.url),
+ exists: true,
+ },
};
it('Returns "content" for Markdown files', () => {
@@ -25,7 +28,7 @@ describe('Content Collections - getEntryType', () => {
});
it('Returns "config" for config files', () => {
- const entry = fileURLToPath(contentPaths.config);
+ const entry = fileURLToPath(contentPaths.config.url);
const type = getEntryType(entry, contentPaths);
expect(type).to.equal('config');
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6a13dde24..6d665afdb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1677,6 +1677,14 @@ importers:
'@astrojs/mdx': link:../../../../integrations/mdx
astro: link:../../..
+ packages/astro/test/fixtures/content-collections-with-config-mjs:
+ specifiers:
+ '@astrojs/mdx': workspace:*
+ astro: workspace:*
+ dependencies:
+ '@astrojs/mdx': link:../../../../integrations/mdx
+ astro: link:../../..
+
packages/astro/test/fixtures/content-ssr-integration:
specifiers:
'@astrojs/mdx': workspace:*