Compare commits
15 commits
main
...
feat/markd
Author | SHA1 | Date | |
---|---|---|---|
|
f55dbf1d45 | ||
|
1a0425820d | ||
|
2b48f10b77 | ||
|
e2dfb1e195 | ||
|
ff53c015bd | ||
|
ad8e937454 | ||
|
c65b92c176 | ||
|
271b45f16b | ||
|
cba4f2bd5c | ||
|
4273c10a38 | ||
|
6ef9664f98 | ||
|
eadf186e42 | ||
|
5c6c6c39bc | ||
|
2aec0b8a6f | ||
|
61ec2c54c0 |
19 changed files with 208 additions and 5 deletions
|
@ -4,6 +4,7 @@ export default {
|
|||
// dist: './dist', // When running `astro build`, path to final static output
|
||||
// public: './public', // A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that don’t need processing.
|
||||
buildOptions: {
|
||||
// draft: true, // Ignore markdown documents marked as draft when running `astro build`
|
||||
// site: 'http://example.com', // Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs.
|
||||
// sitemap: true, // Generate sitemap (set to "false" to disable)
|
||||
},
|
||||
|
|
|
@ -26,6 +26,8 @@ export interface AstroConfig {
|
|||
markdownOptions?: Partial<AstroMarkdownOptions>;
|
||||
/** Options specific to `astro build` */
|
||||
buildOptions: {
|
||||
/** Generate pages from draft documents (set to "true" to enable) **/
|
||||
draft?: boolean;
|
||||
/** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
|
||||
site?: string;
|
||||
/** Generate sitemap (set to "false" to disable) */
|
||||
|
|
|
@ -92,6 +92,7 @@ export async function buildStaticPage({ astroConfig, buildState, filepath, runti
|
|||
const { pages: pagesRoot } = astroConfig;
|
||||
const url = filepath.pathname.replace(pagesRoot.pathname, '/').replace(/(index)?\.(astro|md)$/, '');
|
||||
const result = await runtime.load(url);
|
||||
if (result.statusCode === 403) return;
|
||||
if (result.statusCode !== 200) throw new Error((result as any).error);
|
||||
const outFile = path.posix.join(url, '/index.html');
|
||||
buildState[outFile] = {
|
||||
|
|
|
@ -38,6 +38,9 @@ function validateConfig(config: any): void {
|
|||
throw new Error('[config] buildOptions.site must be a valid URL');
|
||||
}
|
||||
}
|
||||
if (config.buildOptions.drafts !== undefined && typeof config.buildOptions.drafts !== 'boolean') {
|
||||
throw new Error(`[config] buildOptions.drafts: ${JSON.stringify(config.buildOptions.drafts)}\n Expected boolean, received ${type(config.buildOptions.drafts)}.`);
|
||||
}
|
||||
}
|
||||
|
||||
// devOptions
|
||||
|
@ -63,6 +66,7 @@ function configDefaults(userConfig?: any): any {
|
|||
if (!config.buildOptions) config.buildOptions = {};
|
||||
if (!config.markdownOptions) config.markdownOptions = {};
|
||||
if (typeof config.buildOptions.sitemap === 'undefined') config.buildOptions.sitemap = true;
|
||||
if (typeof config.buildOptions.draft === 'undefined') config.buildOptions.draft = false;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,25 @@ export default async function dev(astroConfig: AstroConfig) {
|
|||
res.end();
|
||||
break;
|
||||
}
|
||||
case 403: {
|
||||
const fullurl = new URL(req.url || '/', astroConfig.buildOptions.site || `http://localhost${astroConfig.devOptions.port}`);
|
||||
const reqPath = decodeURI(fullurl.pathname);
|
||||
error(logging, 'static', 'Forbidden', reqPath);
|
||||
res.statusCode = 403;
|
||||
|
||||
const fourOhThreeResult = await runtime.load('/403');
|
||||
if (fourOhThreeResult.statusCode === 200) {
|
||||
if (fourOhThreeResult.contentType) {
|
||||
res.setHeader('Content-Type', fourOhThreeResult.contentType);
|
||||
}
|
||||
res.write(fourOhThreeResult.contents);
|
||||
} else {
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.write(`Forbidden: ${result.error.message}`);
|
||||
}
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
case 404: {
|
||||
const fullurl = new URL(req.url || '/', astroConfig.buildOptions.site || `http://localhost${astroConfig.devOptions.port}`);
|
||||
const reqPath = decodeURI(fullurl.pathname);
|
||||
|
|
|
@ -46,11 +46,12 @@ type LoadResultSuccess = {
|
|||
contents: string | Buffer;
|
||||
contentType?: string | false;
|
||||
};
|
||||
type LoadResultForbidden = { statusCode: 403; error: Error; collectionInfo?: CollectionInfo };
|
||||
type LoadResultNotFound = { statusCode: 404; error: Error; collectionInfo?: CollectionInfo };
|
||||
type LoadResultRedirect = { statusCode: 301 | 302; location: string; collectionInfo?: CollectionInfo };
|
||||
type LoadResultError = { statusCode: 500 } & ({ type: 'parse-error'; error: CompileError } | { type: 'not-found'; error: CompileError } | { type: 'unknown'; error: Error });
|
||||
|
||||
export type LoadResult = (LoadResultSuccess | LoadResultNotFound | LoadResultRedirect | LoadResultError) & { collectionInfo?: CollectionInfo };
|
||||
export type LoadResult = (LoadResultSuccess | LoadResultForbidden | LoadResultNotFound | LoadResultRedirect | LoadResultError) & { collectionInfo?: CollectionInfo };
|
||||
|
||||
// Disable snowpack from writing to stdout/err.
|
||||
configureSnowpackLogger(snowpackLogger);
|
||||
|
@ -105,6 +106,23 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
|
|||
let collection = {} as CollectionResult;
|
||||
let additionalURLs = new Set<string>();
|
||||
|
||||
if (mod.exports.__content) {
|
||||
const hasDraftAttribute = mod.exports.__content.hasOwnProperty('draft');
|
||||
const hasPublishAttribute = mod.exports.__content.hasOwnProperty('published');
|
||||
if (hasPublishAttribute && mod.exports.__content['published'] === false) {
|
||||
return {
|
||||
statusCode: 403,
|
||||
error: new Error('Document is not published')
|
||||
};
|
||||
}
|
||||
if (hasDraftAttribute && mod.exports.__content['draft'] === true && !buildOptions.draft && config.mode === 'production') {
|
||||
return {
|
||||
statusCode: 403,
|
||||
error: new Error('Document is not published')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (mod.exports.createCollection) {
|
||||
const createCollection: CreateCollection = await mod.exports.createCollection();
|
||||
const VALID_KEYS = new Set(['data', 'routes', 'permalink', 'pageSize', 'rss']);
|
||||
|
@ -137,6 +155,10 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
|
|||
let data: any[] = await loadData({ params: currentParams });
|
||||
if (!data) throw new Error(`[createCollection] \`data()\` returned nothing (empty data)"`);
|
||||
if (!Array.isArray(data)) data = [data]; // note: this is supposed to be a little friendlier to the user, but should we error out instead?
|
||||
data = data.filter(entry => !entry.hasOwnProperty('published') || (entry.hasOwnProperty('published') && entry.published));
|
||||
if (!buildOptions.draft && config.mode === "production") {
|
||||
data = data.filter(entry => !entry.hasOwnProperty('draft') || (entry.hasOwnProperty('draft') && !entry.draft));
|
||||
}
|
||||
|
||||
// handle RSS
|
||||
if (createRSS) {
|
||||
|
|
|
@ -115,7 +115,7 @@ export function searchForPage(url: URL, astroConfig: AstroConfig): SearchResult
|
|||
function loadCollection(url: string, astroConfig: AstroConfig): { currentPage?: number; location: PageLocation } | undefined {
|
||||
const pages = glob('**/$*.astro', { cwd: fileURLToPath(astroConfig.pages), filesOnly: true });
|
||||
for (const pageURL of pages) {
|
||||
const reqURL = new RegExp('^/' + pageURL.replace(/\$([^/]+)\.astro/, '$1') + '(?:/(.*)|/?$)');
|
||||
const reqURL = new RegExp('^/' + pageURL.replace(/\$([^/]+)\.astro/, '$1') + '(?:/(.+)|/?$)');
|
||||
const match = url.match(reqURL);
|
||||
if (match) {
|
||||
let currentPage: number | undefined;
|
||||
|
|
|
@ -17,7 +17,7 @@ Collections('shallow selector (*.md)', async ({ runtime }) => {
|
|||
}),
|
||||
];
|
||||
// assert they loaded in newest -> oldest order (not alphabetical)
|
||||
assert.equal(urls, ['/post/three', '/post/two', '/post/one']);
|
||||
assert.equal(urls, ['/post/six', '/post/five', '/post/three', '/post/two', '/post/one']);
|
||||
});
|
||||
|
||||
Collections('deep selector (**/*.md)', async ({ runtime }) => {
|
||||
|
@ -29,7 +29,7 @@ Collections('deep selector (**/*.md)', async ({ runtime }) => {
|
|||
return $(this).attr('href');
|
||||
}),
|
||||
];
|
||||
assert.equal(urls, ['/post/nested/a', '/post/three', '/post/two', '/post/one']);
|
||||
assert.equal(urls, ['/post/nested/a', '/post/six', '/post/five', '/post/three', '/post/two', '/post/one']);
|
||||
});
|
||||
|
||||
Collections('generates pagination successfully', async ({ runtime }) => {
|
||||
|
@ -123,4 +123,18 @@ Collections('matches collection filename exactly', async ({ runtime }) => {
|
|||
assert.equal(urls, ['/post/nested/a', '/post/three', '/post/two', '/post/one']);
|
||||
});
|
||||
|
||||
Collections('have the proper amount of elements with respecting published / draft documents', async ({ runtime }) => {
|
||||
const result = await runtime.load('/paginated');
|
||||
if (result.error) throw new Error(result.error);
|
||||
const $ = doc(result.contents);
|
||||
|
||||
|
||||
const start = $('#start');
|
||||
const end = $('#end');
|
||||
const total = $('#total');
|
||||
assert.equal(start.text(), "0");
|
||||
assert.equal(end.text(), "4");
|
||||
assert.equal(total.text(), "5");
|
||||
});
|
||||
|
||||
Collections.run();
|
||||
|
|
|
@ -22,6 +22,12 @@ export async function createCollection() {
|
|||
))}
|
||||
</div>
|
||||
|
||||
<div id="results">
|
||||
<span id="start">{collection.start}</span>
|
||||
<span id="end">{collection.end}</span>
|
||||
<span id="total">{collection.total}</span>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
{collection.url.prev && <a id="prev-page" href={collection.url.prev}>Previous page</a>}
|
||||
{collection.url.next && <a id="next-page" href={collection.url.next}>Next page</a>}
|
||||
|
|
|
@ -8,7 +8,7 @@ export async function createCollection() {
|
|||
data.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
return data;
|
||||
},
|
||||
pageSize: 4
|
||||
pageSize: 5
|
||||
};
|
||||
}
|
||||
---
|
||||
|
|
9
packages/astro/test/fixtures/astro-collection/src/pages/post/five.md
vendored
Normal file
9
packages/astro/test/fixtures/astro-collection/src/pages/post/five.md
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Post Five
|
||||
date: 2021-04-17 00:00:00
|
||||
draft: false
|
||||
---
|
||||
|
||||
# Post Five
|
||||
|
||||
I’m the fifth blog post
|
9
packages/astro/test/fixtures/astro-collection/src/pages/post/four.md
vendored
Normal file
9
packages/astro/test/fixtures/astro-collection/src/pages/post/four.md
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Post Four
|
||||
date: 2021-04-16 00:00:00
|
||||
published: false
|
||||
---
|
||||
|
||||
# Post Four
|
||||
|
||||
I’m the fourth blog post
|
9
packages/astro/test/fixtures/astro-collection/src/pages/post/six.md
vendored
Normal file
9
packages/astro/test/fixtures/astro-collection/src/pages/post/six.md
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Post Six
|
||||
date: 2021-04-18 00:00:00
|
||||
published: true
|
||||
---
|
||||
|
||||
# Post Six
|
||||
|
||||
I’m the sixth blog post
|
|
@ -2,6 +2,7 @@
|
|||
title: Post Two
|
||||
date: 2021-04-14 00:00:00
|
||||
author: author-two
|
||||
draft: true
|
||||
---
|
||||
|
||||
# Post Two
|
||||
|
|
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-draft.md
vendored
Normal file
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-draft.md
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
layout: ../layouts/content.astro
|
||||
title: My Blog Post
|
||||
description: This is a post about some stuff.
|
||||
draft: true
|
||||
---
|
||||
|
||||
## Interesting Topic
|
||||
|
||||
Hello world!
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
```
|
||||
|
||||
<div id="first">Some content</div>
|
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-no-draft.md
vendored
Normal file
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-no-draft.md
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
layout: ../layouts/content.astro
|
||||
title: My Blog Post
|
||||
description: This is a post about some stuff.
|
||||
draft: false
|
||||
---
|
||||
|
||||
## Interesting Topic
|
||||
|
||||
Hello world!
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
```
|
||||
|
||||
<div id="first">Some content</div>
|
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-published.md
vendored
Normal file
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-published.md
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
layout: ../layouts/content.astro
|
||||
title: My Blog Post
|
||||
description: This is a post about some stuff.
|
||||
published: true
|
||||
---
|
||||
|
||||
## Interesting Topic
|
||||
|
||||
Hello world!
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
```
|
||||
|
||||
<div id="first">Some content</div>
|
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-unpublished.md
vendored
Normal file
18
packages/astro/test/fixtures/plain-markdown/src/pages/post-unpublished.md
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
layout: ../layouts/content.astro
|
||||
title: My Blog Post
|
||||
description: This is a post about some stuff.
|
||||
published: false
|
||||
---
|
||||
|
||||
## Interesting Topic
|
||||
|
||||
Hello world!
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
```
|
||||
|
||||
<div id="first">Some content</div>
|
|
@ -30,6 +30,40 @@ Markdown('Can load a realworld markdown page with Astro', async ({ runtime }) =>
|
|||
assert.equal($('pre').length, 7);
|
||||
});
|
||||
|
||||
Markdown('Cant load a post being unpublished', async ({ runtime }) => {
|
||||
const result = await runtime.load('/post-unpublished');
|
||||
|
||||
assert.type(result.error, 'object')
|
||||
assert.equal(result.error.message, 'Document is not published');
|
||||
assert.equal(result.statusCode, 403);
|
||||
});
|
||||
|
||||
Markdown('Cant load a post being published', async ({ runtime }) => {
|
||||
const result = await runtime.load('/post-published');
|
||||
if (result.error) throw new Error(result.error);
|
||||
|
||||
assert.equal(result.statusCode, 200);
|
||||
|
||||
const $ = doc(result.contents);
|
||||
|
||||
assert.equal($('p').first().text(), 'Hello world!');
|
||||
assert.equal($('#first').text(), 'Some content');
|
||||
assert.equal($('#interesting-topic').text(), 'Interesting Topic');
|
||||
});
|
||||
|
||||
Markdown('Cant load a post being in draft status', async ({ runtime }) => {
|
||||
const result = await runtime.load('/post-draft');
|
||||
if (result.error) throw new Error(result.error);
|
||||
|
||||
assert.equal(result.statusCode, 200);
|
||||
|
||||
const $ = doc(result.contents);
|
||||
|
||||
assert.equal($('p').first().text(), 'Hello world!');
|
||||
assert.equal($('#first').text(), 'Some content');
|
||||
assert.equal($('#interesting-topic').text(), 'Interesting Topic');
|
||||
});
|
||||
|
||||
Markdown('Builds markdown pages for prod', async (context) => {
|
||||
await context.build();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue