Skip to content

Commit 8774106

Browse files
committed
Enable plugin selection through siteConfig.json and reduce repeated generated-asset verification without weakening integrity checks.
1 parent 9820745 commit 8774106

41 files changed

Lines changed: 1356 additions & 117 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎frontend/purestack.studio/guides/extending/plugins.mdx‎

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,49 @@ A plugin is a named object that extends how PureStack builds your site. Each of
2929

3030
## Add your first plugin
3131

32+
To use an installed plugin without writing TypeScript, list its package in
33+
`siteConfig.json`. For example, install `@purestack/ts-images`, then enable image
34+
optimization:
35+
36+
```json
37+
{
38+
"plugins": ["@purestack/ts-images#imagesPlugin"],
39+
"extensions": {
40+
"images": { "widths": [480, 960, 1440] }
41+
}
42+
}
43+
```
44+
45+
Use `package#exportName` to select a named export, for example
46+
`"@purestack/ts-images#imagesPlugin"`. Without a suffix, PureStack selects the
47+
default export. Each selected export must be a plugin object or a no-argument
48+
factory. Packages can expose multiple plugins; list each export separately:
49+
50+
```json
51+
{
52+
"plugins": [
53+
"@purestack/ts-page-scripts#consentPlugin",
54+
"@purestack/ts-page-scripts#analyticsPlugin"
55+
]
56+
}
57+
```
58+
59+
The suffix is the JavaScript export name, not the plugin's `name`. Package
60+
subpaths also work: `package/subpath#exportName`. PureStack loads selections for
61+
builds, publishing, and development, and reloads them when this JSON file changes.
62+
Each plugin owns its settings; listing `imagesPlugin` enables
63+
its defaults even without `extensions.images`. Set `extensions.images.enabled`
64+
to `false` to disable optimization. An explicit plugin instance in
65+
`purestack.config.ts` replaces a JSON-selected plugin with the same name.
66+
67+
Both config formats use PureStack's own copy of a package when available so
68+
shared registries stay together; other packages resolve from the site.
69+
Plugin declarations execute installed JavaScript with the build process's
70+
permissions, so treat changes to `plugins` as code changes even when a CMS edits
71+
other site settings.
72+
73+
For local plugins and custom callbacks, use the TypeScript setup below.
74+
3275
Keep plugins in a folder of their own, beside the content folder:
3376

3477
```text
@@ -111,7 +154,23 @@ Metadata defaults compose through `headConfig({ site, pageInfo })` in plugin ord
111154

112155
Auth hints, consent, and analytics are independent plugins in `ts-page-scripts`. The analytics plugin contributes automatic GA4 consent services during configuration resolution, before any factory or lifecycle hook runs. Omitting it removes automatic registration and direct tracking scripts. Explicitly configured consent services remain available. Analytics never bypasses enabled consent when the consent runtime plugin is omitted.
113156

114-
To choose the standard integrations yourself, disable the automatic set and list the plugins you want. The factories are exported from `purestack`, `@purestack/ts-ssg`, and their individual packages:
157+
To choose the standard integrations yourself, disable the automatic set and list the plugins you want in `siteConfig.json`:
158+
159+
```json
160+
{
161+
"defaultPlugins": false,
162+
"plugins": [
163+
"@purestack/ts-theme#themePlugin",
164+
"@purestack/ts-components#componentsPlugin"
165+
]
166+
}
167+
```
168+
169+
Omitting `defaultPlugins` keeps the automatic set enabled. An explicit
170+
TypeScript/programmatic `defaultPlugins` value overrides the JSON flag.
171+
Development reloads changes to either setting.
172+
173+
The same selection is available in `purestack.config.ts`. The factories are exported from `purestack`, `@purestack/ts-ssg`, and their individual packages:
115174

116175
```ts
117176
import { componentsPlugin, defineConfig, themePlugin } from 'purestack'
@@ -175,7 +234,7 @@ export const brandPlugin = definePlugin({
175234
})
176235
```
177236

178-
`resolveSiteConfig(input, { plugins, defaultPlugins })` composes the same selected plugins as a build, including the standard plugins by default. Plugin-owned sections (`style`, `docLayout`, `pageToc`, `logo`, `auth`, `consent`, `analytics`, `pagefind`, and `preview`) are optional in `SiteConfig`: an omitted plugin does not supply defaults or validate its unused input. A replacement plugin reads its settings from `input` and `file`, then contributes normalized config. Analytics also resolves explicitly configured consent when its browser runtime is omitted, so enabled consent is never bypassed. Configuration contributions are synchronous and may run whenever a project configuration is resolved; keep file writes and other side effects in lifecycle hooks.
237+
`resolveSiteConfig(input, { plugins, defaultPlugins })` composes the supplied plugin instances with the standard plugins by default. This synchronous helper does not apply JSON plugin selection (`plugins` or `defaultPlugins`) or load `purestack.config.ts`. Plugin-owned sections (`style`, `docLayout`, `pageToc`, `logo`, `auth`, `consent`, `analytics`, `pagefind`, and `preview`) are optional in `SiteConfig`: an omitted plugin does not supply defaults or validate its unused input. A replacement plugin reads its settings from `input` and `file`, then contributes normalized config. Analytics also resolves explicitly configured consent when its browser runtime is omitted, so enabled consent is never bypassed. Configuration contributions are synchronous and may run whenever a project configuration is resolved; keep file writes and other side effects in lifecycle hooks.
179238

180239
Builder style state is isolated across asynchronous operations, including concurrent sites. Its owned theme options remain linked to `context.config.style.theme`, so in-place config-hook changes and `themes.setOptions` update page colors, CSS, and generated favicons consistently. Head, metadata, template, Markdown, provider, and lifecycle errors identify their owning plugin and preserve the original cause. Markdown wrappers preserve unified presets, duplicate-plugin option merging, and callback transforms.
181240

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "purestack-repo",
4-
"version": "1.1.15",
4+
"version": "1.1.16",
55
"packageManager": "yarn@4.18.1",
66
"type": "module",
77
"sideEffects": false,

‎packages/purestack/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "purestack",
3-
"version": "1.1.15",
3+
"version": "1.1.16",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/purestack.mjs",
66
"types": "./dist/purestack.d.mts",

‎packages/purestack/src/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export const version: string = '1.1.15'
1+
export const version: string = '1.1.16'
22

33
export * from '@purestack/ts-ssg'

‎packages/ts-common/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-common",
3-
"version": "1.1.15",
3+
"version": "1.1.16",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-common.mjs",
66
"types": "./dist/ts-common.d.mts",

‎packages/ts-components/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-components",
3-
"version": "1.1.15",
3+
"version": "1.1.16",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-components.mjs",
66
"types": "./dist/ts-components.d.mts",

‎packages/ts-css/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-css",
3-
"version": "1.1.15",
3+
"version": "1.1.16",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-css.mjs",
66
"types": "./dist/ts-css.d.mts",

‎packages/ts-highlight/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-highlight",
3-
"version": "1.1.15",
3+
"version": "1.1.16",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-highlight.mjs",
66
"types": "./dist/ts-highlight.d.mts",

‎packages/ts-highlight/src/codeLanguages.test.ts‎

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,30 @@ import {
33
createHljsHighlighter,
44
createMdxHighlighter,
55
DEFAULT_MDX_CODE_LANGS,
6+
type MdxShikiFactory,
67
} from '@purestack/ts-highlight'
78
import hljs from 'highlight.js'
8-
import { describe, expect, it, vi } from 'vitest'
9+
import { createHighlighter, type Highlighter } from 'shiki'
10+
import { afterEach, describe, expect, it, vi } from 'vitest'
911

1012
describe('MDX code languages', () => {
13+
const nativeHighlighters = new Set<Highlighter>()
14+
const createTestShiki: MdxShikiFactory = async (options) => {
15+
const highlighter = await createHighlighter(options)
16+
nativeHighlighters.add(highlighter)
17+
return highlighter
18+
}
19+
afterEach(() => {
20+
for (const highlighter of nativeHighlighters) highlighter.dispose()
21+
nativeHighlighters.clear()
22+
})
23+
1124
it('renders every inferred language with the default Shiki grammars', async () => {
12-
const highlighter = await createMdxHighlighter()
25+
const highlighter = await createMdxHighlighter(
26+
undefined,
27+
undefined,
28+
createTestShiki,
29+
)
1330
const languages = new Set([
1431
...DEFAULT_MDX_CODE_LANGS,
1532
...Object.values(LANGUAGE_BY_EXTENSION),
@@ -48,7 +65,11 @@ describe('MDX code languages', () => {
4865
['gql', 'graphql', 'query { user { id } }'],
4966
])('resolves %s with both highlighters', async (alias, language, code) => {
5067
// Loading by the alias must also work when defaults are overridden.
51-
const shiki = await createMdxHighlighter(undefined, [alias])
68+
const shiki = await createMdxHighlighter(
69+
undefined,
70+
[alias],
71+
createTestShiki,
72+
)
5273
expect(await shiki.codeToHtml(code, alias)).toBe(
5374
await shiki.codeToHtml(code, language),
5475
)

‎packages/ts-html/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-html",
3-
"version": "1.1.15",
3+
"version": "1.1.16",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-html.mjs",
66
"types": "./dist/ts-html.d.mts",

0 commit comments

Comments
 (0)