Merge pull request #25 from MatthiesenXYZ/develop

Chore:  NEW `expressive-code` implementation
This commit is contained in:
Adam Matthiesen 2024-03-07 03:37:35 -08:00 committed by GitHub
commit 9a6fa6b55b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 2695 additions and 755 deletions

View File

@ -0,0 +1,12 @@
---
"@matthiesenxyz/astro-gists": minor
---
BREAKING:
- `@astrojs/mdx` has been removed from the internal integration, please add this back on your own if you need it.
NEW:
- Custom Expressive-Code Implimentation that can run along-side existing installs! Making this package super flexable now!
- You can now customize the code blocks theme by settings the `theme` option in your `astro.config.mjs`, Available options are listed [here](https://shiki.matsu.io/docs/themes)

View File

@ -57,9 +57,7 @@ import { defineConfig } from "astro/config";
// https://astro.build/config
export default defineConfig({
+ integrations: [astroGist({
// OPTIONAL CONFIG OPTIONS
// Enable the Astrojs/MDX Integration - Default: true
MDXIntegration: true
theme: 'github-dark' // OPTIONAL, if not set defaults to Astro's Houston, Only Available options are Shiki Bundled options
+ })]
});
```
@ -89,6 +87,8 @@ This Utility is meant to display a single Gist as Codeblocks using ExpressiveCod
```astro
---
import { GetGist } from "@matthiesenxyz/astro-gists/components"
// OR
import GetGist from "@matthiesenxyz/astro-gists/GetGist"
---
<GetGist
gistId="your-gist-id-here"
@ -103,6 +103,8 @@ This Utility is meant to display an entire collection of Gists by ID
```astro
---
import { GetGistGroup } from "@matthiesenxyz/astro-gists/components"
// OR
import GetGistGroup from "@matthiesenxyz/astro-gists/GetGistGroup"
---
<GetGistGroup
gistId="your-gist-id-here"

View File

@ -1,37 +0,0 @@
/**
* @file This file provides the types for Astro-Gists's `@matthiesenxyz/astro-gists/expressive-code` export.
*/
export * from 'astro-expressive-code';
import type { AstroGistsExpressiveCodeOptions } from './src/integrations/expressive-code';
export type { AstroGistsExpressiveCodeOptions };
/**
* A utility function that helps you define an Expressive Code configuration object. It is meant
* to be used inside the optional config file `ec.config.mjs` located in the root directory
* of your Starlight project, and its return value to be exported as the default export.
*
* Expressive Code will automatically detect this file and use the exported configuration object
* to override its own default settings.
*
* Using this function is recommended, but not required. It just passes through the given object,
* but it also provides type information for your editor's auto-completion and type checking.
*
* @example
* ```js
* // ec.config.mjs
* import { defineEcConfig } from '@matthiesenxyz/astro-gists/expressive-code'
*
* export default defineEcConfig({
* themes: ['github-dark', 'github-light'],
* styleOverrides: {
* borderRadius: '0.5rem',
* },
* })
* ```
*/
export function defineEcConfig(
config: AstroGistsExpressiveCodeOptions
): AstroGistsExpressiveCodeOptions;

View File

@ -1,21 +0,0 @@
/**
* @file This file is exported by astro-gists as `@matthiesenxyz/astro-gists/expressive-code`.
*
* It is required by the `<Code>` component to access the same configuration preprocessor
* function as the one used by the integration.
*
* It also provides access to all of the Expressive Code classes and functions without having
* to install `astro-expressive-code` as an additional dependency into a user's project
* (and thereby risiking version conflicts).
*
* Note: This file is intentionally not a TypeScript module to allow access to all exported
* functionality even if TypeScript is not available, e.g. from the `ec.config.mjs` file
* that does not get processed by Vite.
*/
export * from 'astro-expressive-code';
// @ts-ignore - Types are provided by the separate `expressive-code.d.ts` file
export function defineEcConfig(config) {
return config;
}

View File

@ -1,5 +0,0 @@
/**
* @file This file contains utility functions imported by the `<Code>` component.
*/
export { getGistsEcConfigPreprocessor } from './src/integrations/expressive-code.js';

View File

@ -31,25 +31,21 @@
"sideEffects": false,
"exports": {
".": "./src/index.ts",
"./expressive-code": {
"types": "./expressive-code.d.ts",
"default": "./expressive-code.mjs"
},
"./internal": "./internal.ts",
"./components": "./src/components/index.ts",
"./components/*": "./src/components/*"
"./GetGist": "./src/components/GetGist.astro",
"./GetGistGroup": "./src/components/GetGistGroup.astro"
},
"scripts": {},
"type": "module",
"peerDependencies": {
"astro": "^4.4.0"
"astro": "^4.4.1"
},
"dependencies": {
"@astrojs/mdx": "2.1.1",
"@expressive-code/plugin-line-numbers": "^0.33.4",
"@octokit/types": "^12.6.0",
"astro-expressive-code": "^0.33.4",
"astro-integration-kit": "^0.5.1",
"expressive-code": "^0.33.4",
"hast-util-to-html": "^8.0.4",
"octokit": "^3.1.2",
"vite": "^5.1.5"
}

View File

@ -0,0 +1,75 @@
---
import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers'
import { ExpressiveCode, loadShikiTheme, type BundledShikiTheme, ExpressiveCodeTheme } from 'expressive-code'
import { toHtml } from 'hast-util-to-html'
import fs from 'node:fs'
import config from "virtual:astro-gists/config";
import { getPageData } from './page-data'
const jsoncString = fs.readFileSync(new URL(`../themes/houston.jsonc`, import.meta.url), 'utf-8')
const houston = ExpressiveCodeTheme.fromJSONString(jsoncString)
const theme = config.theme
const { code, raw_url, locale,
lang = '', meta = '', ...props
} = Astro.props
const pageData = getPageData(Astro.request)
// Note: It's important to store the incremented index in a local variable immediately,
// as the `pageData` object is shared between all components on the current page
// and can be changed by other Code components during the `await` calls below
const groupIndex = ++pageData.blockGroupIndex
const engine = new ExpressiveCode({
themes: [theme ? await loadShikiTheme(theme as BundledShikiTheme) : houston],
plugins: [pluginLineNumbers()],
})
const baseStyles = await engine.getBaseStyles();
const themeStyles = await engine.getThemeStyles();
const jsModules = await engine.getJsModules();
const { renderedGroupAst, styles } = await engine.render({
language: lang, meta, locale, code,
parentDocument: {
positionInDocument: { groupIndex,},
},
props, })
let htmlContent = toHtml(renderedGroupAst)
const stylesToPrepend: string[] = []
stylesToPrepend.push(baseStyles, themeStyles, ...styles)
if (stylesToPrepend.length) {
htmlContent = `<style>${[...stylesToPrepend].join('').replace(/\.expressive-code/g, '.gist .expressive-code')}</style>${htmlContent}`
}
const scriptsToPrepend: string[] = []
scriptsToPrepend.push(...jsModules)
if (scriptsToPrepend.length) {
htmlContent = `<script type="module">${[...scriptsToPrepend].join('')}</script>${htmlContent}`
}
---
<div class="gist">
<a class="raw" title="GitHub Gist RAW" href={raw_url}>View <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-filetype-raw" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM1.597 11.85H0v3.999h.782v-1.491h.71l.7 1.491h1.651l.313-1.028h1.336l.314 1.028h.84L5.31 11.85h-.925l-1.329 3.96-.783-1.572A1.18 1.18 0 0 0 3 13.116q0-.384-.167-.668a1.1 1.1 0 0 0-.478-.44 1.7 1.7 0 0 0-.758-.158m-.815 1.913v-1.292h.7a.74.74 0 0 1 .507.17q.194.17.194.49 0 .315-.194.474-.19.158-.518.158zm4.063-1.148.489 1.617H4.32l.49-1.617zm4.006.445-.74 2.789h-.73L6.326 11.85h.855l.601 2.903h.038l.706-2.903h.683l.706 2.903h.04l.596-2.903h.858l-1.055 3.999h-.73l-.74-2.789H8.85Z"/> </svg></a>
<Fragment set:html={htmlContent} />
</div>
<style>
.raw {
display: inline-flex;
position: absolute;
z-index: 99;
right: 0;
color: gray;
padding-top: 2px;
padding-inline-end: 1rem;
text-decoration: none;
}
.gist {
padding-top: 1rem;
padding-bottom: 1rem;
}
</style>

View File

@ -1,6 +1,6 @@
---
import { getGistFile } from "../utils"
import { Code } from 'astro-expressive-code/components'
import CodeBlob from "./CodeBlob.astro"
export interface Props {
/** REQUIRED: Used to define the desired GitHub Gist ID */
@ -17,37 +17,19 @@ export interface Props {
wrap?: boolean;
}
const { gistId, filename, showLineNumbers, wrap } = Astro.props as Props;
const { gistId, filename, wrap, showLineNumbers } = Astro.props as Props;
const SHOWLINENUMBERS = showLineNumbers ? showLineNumbers : showLineNumbers == undefined ? true : false;
const WRAP = wrap ? wrap : wrap == undefined ? true : false;
const SLN = showLineNumbers ? showLineNumbers : showLineNumbers == undefined ? true : false;
const Gist = await getGistFile( gistId, filename);
---
{ Gist &&
<div>
<a class="raw" title="GitHub Gist RAW" href={Gist.raw_url}>View <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-filetype-raw" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM1.597 11.85H0v3.999h.782v-1.491h.71l.7 1.491h1.651l.313-1.028h1.336l.314 1.028h.84L5.31 11.85h-.925l-1.329 3.96-.783-1.572A1.18 1.18 0 0 0 3 13.116q0-.384-.167-.668a1.1 1.1 0 0 0-.478-.44 1.7 1.7 0 0 0-.758-.158m-.815 1.913v-1.292h.7a.74.74 0 0 1 .507.17q.194.17.194.49 0 .315-.194.474-.19.158-.518.158zm4.063-1.148.489 1.617H4.32l.49-1.617zm4.006.445-.74 2.789h-.73L6.326 11.85h.855l.601 2.903h.038l.706-2.903h.683l.706 2.903h.04l.596-2.903h.858l-1.055 3.999h-.73l-.74-2.789H8.85Z"/> </svg></a>
<Code
showLineNumbers={SHOWLINENUMBERS}
wrap={WRAP}
title={Gist.filename}
<CodeBlob
wrap={WRAP} showLineNumbers={SLN}
title={Gist.filename} raw_url={Gist.raw_url}
code={ Gist.content ? Gist.content : "" }
lang={Gist.language ? Gist.language.toLowerCase() : undefined }
/>
</div>
}
<style>
.raw {
display: inline-flex;
position: absolute;
z-index: 99;
right: 0;
color: white;
padding-top: 2px;
padding-inline-end: 1rem;
text-decoration: none;
}
</style>

View File

@ -1,6 +1,6 @@
---
import { getGistGroup } from "../utils"
import { GetGist } from "./index"
import CodeBlob from "./CodeBlob.astro";
export interface Props {
/** REQUIRED: Used to define the desired GitHub Gist ID */
@ -15,38 +15,28 @@ export interface Props {
wrap?: boolean;
}
const { gistId, showLineNumbers, wrap } = Astro.props as Props;
const { gistId, wrap, showLineNumbers } = Astro.props as Props;
const SHOWLINENUMBERS = showLineNumbers ? showLineNumbers : showLineNumbers == undefined ? true : false;
const WRAP = wrap ? wrap : wrap == undefined ? true : false;
const SLN = showLineNumbers ? showLineNumbers : showLineNumbers == undefined ? true : false;
const Gist = await getGistGroup(gistId);
const files = Gist?.files;
const filed = Object.keys(files as object);
const files = Gist.files;
---
{ Gist && (
<div class="gist">
{ filed.map((file) => (
<div class="gist">
<GetGist
gistId={gistId}
filename={file}
showLineNumbers={SHOWLINENUMBERS}
wrap={WRAP}
<div>
{Object.keys(files).map((file) => {
const { content, filename, language, raw_url } = files[file];
return (
<CodeBlob
title={filename} wrap={WRAP}
code={content} raw_url={raw_url}
lang={language.toLowerCase()}
showLineNumbers={SLN}
/>
</div>
))}
);
})}
</div>
) }
<style>
.gist {
padding-top: 1rem;
padding-bottom: 1rem;
}
</style>

View File

@ -1,2 +1,2 @@
export { default as GetGist} from "./GetGist.astro"
export { default as GetGistGroup} from "./GetGistGroup.astro"
export { default as GetGist} from "./GetGist.astro";
export { default as GetGistGroup} from "./GetGistGroup.astro";

View File

@ -0,0 +1,18 @@
export type PageData = {
url: string
blockGroupIndex: number
}
const pageDataMap = new Map<Request, PageData>()
export function getPageData(request: Request): PageData {
let data = pageDataMap.get(request)
if (!data) {
data = {
url: request.url,
blockGroupIndex: -1,
}
pageDataMap.set(request, data)
}
return data
}

View File

@ -1,3 +1,3 @@
import astroGist from "./integration.js";
import astroGist from "./integration";
export default astroGist;

View File

@ -1,13 +1,23 @@
import { defineIntegration, createResolver } from "astro-integration-kit"
import { corePlugins } from "astro-integration-kit/plugins"
import { astroGistsExpressiveCode } from "./integrations/expressive-code"
import { z } from "astro/zod";
import mdx from "@astrojs/mdx";
import { loadEnv } from "vite";
import { type BundledShikiTheme } from 'expressive-code'
// Load environment variables
const { GITHUB_PERSONAL_TOKEN } = loadEnv("all", process.cwd(), "GITHUB_");
export const optionsSchema = z.object({
/**
* Optional: Allows the user to change the default theme for the code blocks.
*
* All available themes are listed in the [Shiki documentation](https://shiki.matsu.io/docs/themes).
*/
theme: z.custom<BundledShikiTheme>().optional(),
});
export type UserConfig = z.infer<typeof optionsSchema>
/** Astro-Gist - An Astro.js integration for embedding GitHub Gists in your Astro.js project.
* @example
* import { defineConfig } from "astro/config";
@ -16,26 +26,23 @@ const { GITHUB_PERSONAL_TOKEN } = loadEnv("all", process.cwd(), "GITHUB_");
* export default defineConfig({
* integrations: [
* astroGist({
* // Enable the Astrojs/MDX Integration - Default: true
* MDXIntegration: true
* // Optional: Change the default theme for the code blocks.
* // Default: `Astro Houston (Custom)` If you want to use the default theme, you can omit this option.
* theme: "github-dark"
* })
* ]
* });
*/
export default defineIntegration({
name: "@matthiesenxyz/astro-gists",
optionsSchema: z.object({
/** Enables the astrojs/mdx Integration */
MDXIntegration: z.boolean().optional().default(true),
}),
optionsSchema,
plugins: [...corePlugins],
setup({ options }) {
const { resolve } = createResolver(import.meta.url)
return {
"astro:config:setup": ({
watchIntegration, hasIntegration,
updateConfig, logger, config
watchIntegration, addVirtualImports, logger,
}) => {
logger.info("Setting up Astro Gists Integration.")
const configSetup = logger.fork("astro-gists/config:setup")
@ -48,38 +55,11 @@ export default defineIntegration({
configSetup.warn("GITHUB_PERSONAL_TOKEN not found. Please add it to your .env file. Without it, you will be limited to 60 requests per hour.")
}
// CHECK FOR EXISTING INTEGRATIONS
const integrations = [...config.integrations]
// Add virtual imports
addVirtualImports({
"virtual:astro-gists/config": `export default ${JSON.stringify(options)}`,
});
// ADD ExpressiveCode INTEGRATION
if (!hasIntegration("astro-expressive-code")) {
configSetup.info("Loading Astro Gists Expressive Code Integration.")
integrations.push(...astroGistsExpressiveCode())
} else {
configSetup.info("Astro Expressive Code Integration already loaded.")
}
// ADD MDX INTEGRATION IF ENABLED
if (options.MDXIntegration && hasIntegration("@astrojs/mdx")) {
configSetup.warn("@astrojs/mdx Integration already loaded.In some cases this could cause issues. Please remove it from your astro.config.mjs file. as it will be added automatically.")
}
if (options.MDXIntegration && !hasIntegration("@astrojs/mdx")) {
configSetup.info("Loading @astrojs/mdx Integration.")
integrations.push(mdx())
}
if (!options.MDXIntegration) {
configSetup.info("Internal MDX Integration Disabled. Skipping...")
}
// UPDATE All integrations
try {
updateConfig({
integrations: [...astroGistsExpressiveCode(), mdx()]
})
} catch (e) {
logger.error(e as string);
throw `@matthiesenxyz/astro-gists: Unable to Update Integrations...\n${e}`;
}
},
"astro:config:done": ({ logger }) => {
const configDone = logger.fork("astro-gists/config:done")

View File

@ -1,44 +0,0 @@
import {
astroExpressiveCode,
type AstroExpressiveCodeOptions,
type CustomConfigPreprocessors
} from 'astro-expressive-code';
import type { AstroIntegration } from 'astro';
import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers'
/**
* Create an Expressive Code configuration preprocessor based on Starlight config.
* Used internally to set up Expressive Code and by the `<Code>` component.
*/
export function getGistsEcConfigPreprocessor(): CustomConfigPreprocessors['preprocessAstroIntegrationConfig'] {
return (input): AstroExpressiveCodeOptions => {
const ecConfig = input.ecConfig as AstroExpressiveCodeOptions; // Replace {} with the appropriate value for ecConfig
const {
themes = ["github-dark", "github-light"] as const,
plugins = [pluginLineNumbers()],
...rest
} = ecConfig;
return {
themes,
plugins,
...rest,
};
};
}
export function astroGistsExpressiveCode(): AstroIntegration[] {
return [
astroExpressiveCode({
customConfigPreprocessors: {
preprocessAstroIntegrationConfig: getGistsEcConfigPreprocessor(),
preprocessComponentConfig: `
import { getGistsEcConfigPreprocessor } from '@matthiesenxyz/astro-gists/internal'
export default getGistsEcConfigPreprocessor()
`,
},
}),
];
};

File diff suppressed because it is too large Load Diff

View File

@ -57,12 +57,7 @@ export const getGistFile = async (
return null;
};
export const getGistGroup = async (
gistId: string
) => {
export const getGistGroup = async (gistId: string) => {
const gist = await getGist(gistId);
if (gist?.files) {
return gist ? gist : null;
}
return null;
return gist;
};

4
package/virtual-config.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module "virtual:astro-gists/config" {
const Config: import("./src/integration").UserConfig;
export default Config;
}

View File

@ -1,7 +1,8 @@
import { defineConfig } from "astro/config";
import astroGist from "@matthiesenxyz/astro-gists";
import mdx from "@astrojs/mdx"
// https://astro.build/config
export default defineConfig({
integrations: [astroGist()]
integrations: [astroGist(),mdx()]
});

View File

@ -15,6 +15,7 @@
"astro": "^4.4.11"
},
"devDependencies": {
"@astrojs/mdx": "^2.1.1",
"@astrojs/check": "^0.5.6",
"@types/node": "^20.11.24",
"typescript": "^5.3.3"

View File

@ -1,7 +1,9 @@
---
import { GetGist, GetGistGroup } from "@matthiesenxyz/astro-gists/components"
import { GetGistGroup } from "@matthiesenxyz/astro-gists/components"
import GetGist from "@matthiesenxyz/astro-gists/GetGist"
---
<h1>Dev: Playground</h1>
<h1>Dev: Playground (Basic Test)</h1>
<GetGist
gistId="cce7f3f1d9322710be8196aa344186ba"

View File

@ -1,15 +0,0 @@
---
title: Hello, World
---
import { GetGist, GetGistGroup } from "@matthiesenxyz/astro-gists/components"
# Hi there! This is a MDX test page.
<GetGist
gistId="cce7f3f1d9322710be8196aa344186ba"
filename="hello.md"
/>
<GetGistGroup
gistId="84243fa11bf96a59bfb237152eb52fa7"
/>

View File

@ -0,0 +1,17 @@
---
title: "Dev: Playground (MDX Test)"
---
import { GetGistGroup } from "@matthiesenxyz/astro-gists/components"
import GetGist from "@matthiesenxyz/astro-gists/GetGist"
<h1>{frontmatter.title}</h1>
<GetGist
gistId="cce7f3f1d9322710be8196aa344186ba"
filename="hello.md"
/>
<GetGistGroup
gistId="84243fa11bf96a59bfb237152eb52fa7"
/>

File diff suppressed because it is too large Load Diff