Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file generation and flag for content intellisense #11639

Merged
merged 30 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c103902
feat: add type to infer input type of collection
Princesseuh Jul 29, 2024
07e8c0e
refactor:
Princesseuh Aug 6, 2024
501a695
Merge branch 'main' into feat/content-collections-intellisense
Princesseuh Aug 6, 2024
591e337
feat: generate json schema for content too
Princesseuh Aug 6, 2024
bd05cd5
feat: generate a manifest of all the collections
Princesseuh Aug 7, 2024
1659ce2
refactor: unnecessary type
Princesseuh Aug 7, 2024
ae5467a
Merge branch 'main' into feat/content-collections-intellisense
Princesseuh Aug 8, 2024
188ce03
fix: only add content collections to manifest
Princesseuh Aug 8, 2024
fac3c34
Merge branch 'main' into feat/content-collections-intellisense
Princesseuh Aug 8, 2024
5044fc2
chore: changeset
Princesseuh Aug 8, 2024
d02f0b7
fix: generate file URLs
Princesseuh Aug 8, 2024
5205346
fix: flag it properly
Princesseuh Aug 8, 2024
7f98b61
fix: save in lower case
Princesseuh Aug 8, 2024
1390593
docs: add jsdoc to experimental option
Princesseuh Aug 9, 2024
8df9759
Merge branch 'content-layer' into feat/content-collections-intellisense
Princesseuh Aug 9, 2024
0d313f6
nit: move function out
Princesseuh Aug 9, 2024
d1f8cc8
Merge branch 'content-layer' into feat/content-collections-intellisense
ascorbic Aug 12, 2024
0461559
fix: match vscode flag name
Princesseuh Aug 12, 2024
3f25212
Update packages/astro/src/@types/astro.ts
Princesseuh Aug 12, 2024
a8ab210
Update packages/astro/src/@types/astro.ts
Princesseuh Aug 12, 2024
dabf08a
Update serious-pumas-run.md
Princesseuh Aug 12, 2024
0d2262f
test: add tests
Princesseuh Aug 12, 2024
f77959b
Add content layer support
ascorbic Aug 13, 2024
d9a26f7
Apply suggestions from code review
Princesseuh Aug 13, 2024
07eaff5
fix: test
Princesseuh Aug 13, 2024
a365604
Merge branch 'content-layer' into feat/content-collections-intellisense
Princesseuh Aug 13, 2024
c5a8530
Update .changeset/serious-pumas-run.md
Princesseuh Aug 13, 2024
e88ca5a
Apply suggestions from code review
Princesseuh Aug 13, 2024
917e159
Remove check for json
ascorbic Aug 14, 2024
4d87be8
Merge branch 'content-layer' into feat/content-collections-intellisense
Princesseuh Aug 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/serious-pumas-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': minor
---

Adds supoprt for content collectioni intellisenes
16 changes: 13 additions & 3 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { OutgoingHttpHeaders } from 'node:http';
import type { AddressInfo } from 'node:net';
import type {
MarkdownHeading,
MarkdownVFile,
Expand All @@ -9,6 +7,8 @@ import type {
ShikiConfig,
} from '@astrojs/markdown-remark';
import type * as babel from '@babel/core';
import type { OutgoingHttpHeaders } from 'node:http';
import type { AddressInfo } from 'node:net';
import type * as rollup from 'rollup';
import type * as vite from 'vite';
import type {
Expand Down Expand Up @@ -78,7 +78,7 @@ export type {
UnresolvedImageTransform,
} from '../assets/types.js';
export type { RemotePattern } from '../assets/utils/remotePattern.js';
export type { SSRManifest, AssetsPrefix } from '../core/app/types.js';
export type { AssetsPrefix, SSRManifest } from '../core/app/types.js';
export type {
AstroCookieGetOptions,
AstroCookieSetOptions,
Expand Down Expand Up @@ -2183,6 +2183,16 @@ export interface AstroUserConfig {
* For a complete overview, and to give feedback on this experimental API, see the [Server Islands RFC](https://github.com/withastro/roadmap/pull/963).
*/
serverIslands?: boolean;

/**
* @docs
* @name experimental.contentCollectionIntellisense
* @type {boolean}
Princesseuh marked this conversation as resolved.
Show resolved Hide resolved
* @default `false`
* @version 4.14.0
* @description
*/
contentCollectionIntellisense?: boolean;
};
}

Expand Down
130 changes: 97 additions & 33 deletions packages/astro/src/content/types-generator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import glob from 'fast-glob';
import { bold, cyan } from 'kleur/colors';
import type fsMod from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import glob from 'fast-glob';
import { bold, cyan } from 'kleur/colors';
import { type ViteDevServer, normalizePath } from 'vite';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
Expand All @@ -13,6 +13,7 @@ import type { Logger } from '../core/logger/core.js';
import { isRelativePath } from '../core/path.js';
import { CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js';
import {
type CollectionConfig,
type ContentConfig,
type ContentObservable,
type ContentPaths,
Expand Down Expand Up @@ -391,6 +392,8 @@ async function writeContentFiles({
entries: {},
};
}

let contentCollectionsMap: CollectionEntryMap = {};
Princesseuh marked this conversation as resolved.
Show resolved Hide resolved
for (const collectionKey of Object.keys(collectionEntryMap).sort()) {
const collectionConfig = contentConfig?.collections[JSON.parse(collectionKey)];
const collection = collectionEntryMap[collectionKey];
Expand Down Expand Up @@ -445,6 +448,22 @@ async function writeContentFiles({
contentTypesStr += `${entryKey}: {\n id: ${entryKey};\n slug: ${slugType};\n body: string;\n collection: ${collectionKey};\n data: ${dataType}\n} & ${renderType};\n`;
}
contentTypesStr += `};\n`;

if (
collectionConfig?.schema &&
settings.config.experimental.contentCollectionIntellisense
) {
await generateJSONSchema(
fs,
collectionConfig,
collectionKey,
collectionSchemasDir,
logger,
);

contentCollectionsMap[collectionKey] = collection;
}

break;
case 'data':
if (collectionEntryKeys.length === 0) {
Expand All @@ -458,42 +477,47 @@ async function writeContentFiles({
}

if (collectionConfig?.schema) {
let zodSchemaForJson =
typeof collectionConfig.schema === 'function'
? collectionConfig.schema({ image: () => z.string() })
: collectionConfig.schema;
if (zodSchemaForJson instanceof z.ZodObject) {
zodSchemaForJson = zodSchemaForJson.extend({
$schema: z.string().optional(),
});
}
try {
await fs.promises.writeFile(
new URL(`./${collectionKey.replace(/"/g, '')}.schema.json`, collectionSchemasDir),
JSON.stringify(
zodToJsonSchema(zodSchemaForJson, {
name: collectionKey.replace(/"/g, ''),
markdownDescription: true,
errorMessages: true,
// Fix for https://github.com/StefanTerdell/zod-to-json-schema/issues/110
dateStrategy: ['format:date-time', 'format:date', 'integer'],
}),
null,
2,
),
);
} catch (err) {
// This should error gracefully and not crash the dev server
logger.warn(
'content',
`An error was encountered while creating the JSON schema for the ${collectionKey} collection. Proceeding without it. Error: ${err}`,
);
}
await generateJSONSchema(
fs,
collectionConfig,
collectionKey,
collectionSchemasDir,
logger,
);
}
break;
}
}

let contentCollectionManifest: {
collections: { hasSchema: boolean; name: string }[];
entries: Record<string, string>;
} = {
collections: [],
entries: {},
};
Object.entries(contentCollectionsMap).forEach(([collectionKey, collection]) => {
const collectionConfig = contentConfig?.collections[JSON.parse(collectionKey)];
const key = JSON.parse(collectionKey);

contentCollectionManifest.collections.push({
hasSchema: Boolean(collectionConfig?.schema),
name: key,
});

Object.keys(collection.entries).forEach((entryKey) => {
const entryPath = fileURLToPath(
new URL(JSON.parse(entryKey), contentPaths.contentDir + `${key}/`),
);
contentCollectionManifest.entries[entryPath] = key;
});
});

await fs.promises.writeFile(
new URL('./collections.json', collectionSchemasDir),
JSON.stringify(contentCollectionManifest, null, 2),
);

if (!fs.existsSync(settings.dotAstroDir)) {
fs.mkdirSync(settings.dotAstroDir, { recursive: true });
}
Expand All @@ -520,3 +544,43 @@ async function writeContentFiles({
typeTemplateContent,
);
}

async function generateJSONSchema(
fsMod: typeof import('node:fs'),
collectionConfig: CollectionConfig,
collectionKey: string,
collectionSchemasDir: URL,
logger: Logger,
) {
let zodSchemaForJson =
typeof collectionConfig.schema === 'function'
? collectionConfig.schema({ image: () => z.string() })
: collectionConfig.schema;
if (zodSchemaForJson instanceof z.ZodObject) {
zodSchemaForJson = zodSchemaForJson.extend({
$schema: z.string().optional(),
});
}
try {
await fsMod.promises.writeFile(
new URL(`./${collectionKey.replace(/"/g, '')}.schema.json`, collectionSchemasDir),
JSON.stringify(
zodToJsonSchema(zodSchemaForJson, {
name: collectionKey.replace(/"/g, ''),
markdownDescription: true,
errorMessages: true,
// Fix for https://github.com/StefanTerdell/zod-to-json-schema/issues/110
dateStrategy: ['format:date-time', 'format:date', 'integer'],
}),
null,
2,
),
);
} catch (err) {
// This should error gracefully and not crash the dev server
logger.warn(
'content',
`An error was encountered while creating the JSON schema for the ${collectionKey} collection. Proceeding without it. Error: ${err}`,
);
}
}
5 changes: 5 additions & 0 deletions packages/astro/src/core/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const ASTRO_CONFIG_DEFAULTS = {
clientPrerender: false,
globalRoutePriority: false,
serverIslands: false,
contentCollectionIntellisense: false,
env: {
validateSecrets: false,
},
Expand Down Expand Up @@ -538,6 +539,10 @@ export const AstroConfigSchema = z.object({
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.serverIslands),
contentCollectionIntellisense: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.contentCollectionIntellisense),
})
.strict(
`Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.`,
Expand Down
Loading